├── .coveragerc ├── .env.example ├── .github └── workflows │ └── release.yaml ├── .gitignore ├── .gitlab-ci.yml ├── .gitlab ├── build-image.yml ├── build-vue.yml ├── github-export.yml ├── pre-commit.yml ├── run-tests.yml ├── sentry.yml ├── test-image.yml └── test-js.yml ├── .pre-commit-config.yaml ├── CHANGELOG.md ├── Dockerfile ├── LICENSE ├── README.rst ├── RELEASES.yaml ├── apps ├── activity │ ├── __init__.py │ ├── admin.py │ ├── apps.py │ ├── migrations │ │ ├── 0001_initial.py │ │ ├── 0002_useractivity_plural.py │ │ └── __init__.py │ ├── models.py │ ├── signals.py │ ├── tests.py │ └── views.py ├── annotations │ ├── __init__.py │ ├── admin.py │ ├── apps.py │ ├── fake_data.py │ ├── migrations │ │ ├── 0001_initial.py │ │ ├── 0002_no_title_no_report_type.py │ │ ├── 0003_annotation_level.py │ │ ├── 0004_annotation_owner_level.py │ │ └── __init__.py │ ├── models.py │ ├── serializers.py │ ├── tests │ │ ├── __init__.py │ │ ├── conftest.py │ │ ├── test_authorization.py │ │ └── test_views.py │ ├── translation.py │ ├── urls.py │ └── views.py ├── api │ ├── __init__.py │ ├── admin.py │ ├── apps.py │ ├── auth.py │ ├── fake_data.py │ ├── migrations │ │ ├── 0001_initial.py │ │ ├── 0002_requirement_update.py │ │ └── __init__.py │ ├── models.py │ ├── permissions.py │ ├── templates │ │ └── api │ │ │ └── redoc.html │ ├── tests │ │ ├── __init__.py │ │ ├── conftest.py │ │ ├── test_api.py │ │ └── test_platform_report_apiview_with_sushi.py │ ├── throttling.py │ ├── urls.py │ └── views.py ├── charts │ ├── __init__.py │ ├── admin.py │ ├── apps.py │ ├── fake_data.py │ ├── logic │ │ └── __init__.py │ ├── migrations │ │ ├── 0001_initial.py │ │ ├── 0002_chartdefinition_reportviewtocharttype.py │ │ ├── 0003_chartdefinition_ignore_organization.py │ │ ├── 0004_is_standard_view_and_position.py │ │ ├── 0005_chartdefinition_ignore_platform.py │ │ ├── 0006_chartdefinition_scope.py │ │ ├── 0007_chartdefinition_scope_blank.py │ │ ├── 0008_datasource_set_null.py │ │ ├── 0009_jsonfield.py │ │ ├── 0010_chartdefinition_is_generic.py │ │ ├── 0011_remove_reportdataview_primary_dimension.py │ │ ├── 0012_tr_j1_unique_item_requests.py │ │ ├── 0013_redefine_standard_views.py │ │ └── __init__.py │ ├── models.py │ ├── serializers.py │ ├── tests │ │ ├── __init__.py │ │ └── test_api.py │ ├── translation.py │ ├── urls.py │ └── views.py ├── conftest.py ├── core │ ├── __init__.py │ ├── account.py │ ├── admin.py │ ├── admin_site.py │ ├── apps.py │ ├── auth.py │ ├── authentication.py │ ├── context_managers.py │ ├── db.py │ ├── exceptions.py │ ├── fake_data.py │ ├── fields.py │ ├── filters.py │ ├── logic │ │ ├── __init__.py │ │ ├── bins.py │ │ ├── dates.py │ │ ├── debug.py │ │ ├── email.py │ │ ├── error_reporting.py │ │ ├── lookup_json_by_title.py │ │ ├── management_commands.py │ │ ├── maximus_sync.py │ │ ├── serialization.py │ │ ├── sync.py │ │ ├── type_conversion.py │ │ ├── url.py │ │ └── util.py │ ├── management │ │ ├── __init__.py │ │ └── commands │ │ │ ├── __init__.py │ │ │ ├── cleanup_database.py │ │ │ ├── create_users_from_csv.py │ │ │ ├── echo.py │ │ │ ├── erms_sync_identities_and_users.py │ │ │ ├── lookup_json_by_title.py │ │ │ ├── start_celery_task.py │ │ │ ├── sync_logging_db.py │ │ │ └── sync_maximus.py │ ├── middleware.py │ ├── migrations │ │ ├── 0001_initial.py │ │ ├── 0002_identity.py │ │ ├── 0003_datasource.py │ │ ├── 0004_identity_verbosename.py │ │ ├── 0005_user_language.py │ │ ├── 0006_blank_ext_id.py │ │ ├── 0007_timestamps.py │ │ ├── 0008_user_extra_data.py │ │ ├── 0009_user_extra_data_default.py │ │ ├── 0010_default_lang_change.py │ │ ├── 0011_datasource_knowledgebase.py │ │ ├── 0012_datasource_set_null.py │ │ ├── 0013_jsonfield.py │ │ ├── 0014_auto_20210329_1002.py │ │ ├── 0015_unique_name_within_organization_data_source.py │ │ ├── 0016_taskprogress.py │ │ ├── 0017_user_extra_data_dismissed_and_seen_last_release.py │ │ ├── 0018_pgcrypto.py │ │ ├── 0019_alter_user_managers.py │ │ ├── 0020_otp_devices.py │ │ ├── 0021_user_skip_2fa.py │ │ └── __init__.py │ ├── models.py │ ├── pagination.py │ ├── permissions.py │ ├── prometheus.py │ ├── request_logging │ │ ├── __init__.py │ │ ├── capture.py │ │ ├── celery_capture.py │ │ └── clickhouse.py │ ├── serializers.py │ ├── signals.py │ ├── static │ │ └── __do_not_remove__.txt │ ├── task_support.py │ ├── tasks.py │ ├── templates │ │ ├── account │ │ │ └── email │ │ │ │ ├── email_confirmation_message.txt │ │ │ │ ├── email_confirmation_signup_message.txt │ │ │ │ ├── email_confirmation_signup_subject.txt │ │ │ │ └── email_confirmation_subject.txt │ │ └── registration │ │ │ ├── invitation_email.html │ │ │ ├── invitation_subject.txt │ │ │ ├── password_reset_message.html │ │ │ └── password_reset_subject.txt │ ├── tests │ │ ├── __init__.py │ │ ├── conftest.py │ │ ├── test_admin.py │ │ ├── test_api.py │ │ ├── test_authentication.py │ │ ├── test_logic_bins.py │ │ ├── test_logic_dates.py │ │ ├── test_lookup_json_by_title.py │ │ ├── test_maximus_sync.py │ │ ├── test_models.py │ │ ├── test_request_logging.py │ │ ├── test_sync.py │ │ └── test_user_management.py │ ├── urls.py │ ├── validators.py │ └── views.py ├── cost │ ├── __init__.py │ ├── admin.py │ ├── apps.py │ ├── migrations │ │ ├── 0001_initial.py │ │ ├── 0002_payment_unique_together.py │ │ └── __init__.py │ ├── models.py │ ├── serializers.py │ ├── urls.py │ └── views.py ├── deployment │ ├── __init__.py │ ├── admin.py │ ├── apps.py │ ├── migrations │ │ ├── 0001_initial.py │ │ ├── 0002_image_as_filefield.py │ │ └── __init__.py │ ├── models.py │ ├── serializers.py │ ├── tests │ │ ├── __init__.py │ │ └── test_api.py │ ├── urls.py │ └── views.py ├── erms │ ├── __init__.py │ ├── api.py │ ├── sync.py │ └── tests │ │ ├── conftest.py │ │ └── test_erms.py ├── events │ ├── __init__.py │ ├── admin.py │ ├── apps.py │ ├── event_filters.py │ ├── fake_data.py │ ├── migrations │ │ ├── 0001_initial.py │ │ ├── 0002_alter_event_last_updated_by.py │ │ ├── 0003_initial_info_event.py │ │ └── __init__.py │ ├── models.py │ ├── serializers.py │ ├── signals.py │ ├── tasks.py │ ├── templates │ │ └── events │ │ │ ├── welcome_event_description.md │ │ │ └── welcome_event_title.txt │ ├── tests │ │ ├── __init__.py │ │ ├── conftest.py │ │ ├── test_api.py │ │ ├── test_models.py │ │ └── test_signals.py │ ├── urls.py │ └── views.py ├── export │ ├── __init__.py │ ├── admin.py │ ├── apps.py │ ├── enums.py │ ├── migrations │ │ ├── 0001_initial.py │ │ ├── 0002_auto_20210329_1002.py │ │ ├── 0003_created_autoaddnow.py │ │ ├── 0004_export_status_error.py │ │ ├── 0005_flexibledataexport_file_format.py │ │ ├── 0006_alter_flexibledataexport_file_format.py │ │ ├── 0007_flexibledataapiexport.py │ │ └── __init__.py │ ├── models.py │ ├── serializers.py │ ├── signals.py │ ├── tasks.py │ ├── tests │ │ ├── __init__.py │ │ ├── conftest.py │ │ ├── test_api_and_tasks.py │ │ └── test_flexibledataexport.py │ ├── urls.py │ └── views.py ├── impersonate_api │ ├── __init__.py │ ├── apps.py │ ├── permissions.py │ ├── serializers.py │ ├── tests │ │ └── test_api.py │ ├── urls.py │ └── views.py ├── knowledgebase │ ├── __init__.py │ ├── admin.py │ ├── apps.py │ ├── management │ │ ├── __init__.py │ │ └── commands │ │ │ ├── __init__.py │ │ │ └── fetch_platforms_from_knowledgebase.py │ ├── migrations │ │ ├── 0001_initial.py │ │ ├── 0002_jsonfield.py │ │ ├── 0003_routersyncattempt.py │ │ ├── 0004_report_type_in_knowledgebase.py │ │ ├── 0005_nibbler.py │ │ ├── 0006_alter_routersyncattempt_last_error.py │ │ └── __init__.py │ ├── models.py │ ├── serializers.py │ ├── signals.py │ ├── tasks.py │ ├── templates │ │ └── knowledgebase │ │ │ └── importattempt_changelist.html │ └── tests │ │ ├── __init__.py │ │ ├── test_models.py │ │ └── test_tasks.py ├── logs │ ├── __init__.py │ ├── admin.py │ ├── apps.py │ ├── constants.py │ ├── cubes.py │ ├── exceptions.py │ ├── fake_data.py │ ├── fields.py │ ├── filters.py │ ├── logic │ │ ├── __init__.py │ │ ├── attempt_import.py │ │ ├── cleanup.py │ │ ├── clickhouse.py │ │ ├── custom_import.py │ │ ├── data_coverage.py │ │ ├── data_import.py │ │ ├── export.py │ │ ├── export_analytical.py │ │ ├── export_counter.py │ │ ├── export_utils.py │ │ ├── interest.py │ │ ├── materialized_interest.py │ │ ├── materialized_reports.py │ │ ├── queries.py │ │ ├── remap.py │ │ ├── reporting │ │ │ ├── __init__.py │ │ │ ├── export.py │ │ │ ├── filters.py │ │ │ ├── helpers.py │ │ │ └── slicer.py │ │ └── split_fetch_intentions.py │ ├── management │ │ ├── __init__.py │ │ └── commands │ │ │ ├── __init__.py │ │ │ ├── check_interest_definitions.py │ │ │ ├── check_materialized_report_types.py │ │ │ ├── check_organizationplatforms.py │ │ │ ├── check_report_type_dimensions.py │ │ │ ├── compare_db_with_clickhouse.py │ │ │ ├── compare_db_with_clickhouse_low_level.py │ │ │ ├── convert_clickhouse_to_mergetree.py │ │ │ ├── define_interest_for_all_platforms.py │ │ │ ├── export_analytical.py │ │ │ ├── export_fetch_attempts.py │ │ │ ├── fetch_sushi5.py │ │ │ ├── find_ibs_without_date.py │ │ │ ├── find_split_records.py │ │ │ ├── import_fetch_attempts.py │ │ │ ├── recompute_interest.py │ │ │ ├── recompute_materialized_reports.py │ │ │ ├── remove_interest.py │ │ │ ├── reporting_stats.py │ │ │ ├── sync_clickhouse_db.py │ │ │ ├── sync_interest.py │ │ │ ├── sync_with_clickhouse.py │ │ │ ├── test_export.py │ │ │ ├── test_materialization_speed.py │ │ │ └── title_reimport.py │ ├── migrations │ │ ├── 0001_initial.py │ │ ├── 0002_add_dimension_sorting.py │ │ ├── 0003_split_accesslog_source.py │ │ ├── 0004_source_field.py │ │ ├── 0005_metric_active.py │ │ ├── 0006_metric_interest_group.py │ │ ├── 0007_name_in_interest_group.py │ │ ├── 0008_importbatch.py │ │ ├── 0009_accesslog_import_batch.py │ │ ├── 0010_verbose_name_plural.py │ │ ├── 0011_richer_importbatch.py │ │ ├── 0012_manualdataupload.py │ │ ├── 0013_nullable_title_mdu_validation.py │ │ ├── 0014_default_dimension_type.py │ │ ├── 0015_change_data_file_validators.py │ │ ├── 0016_report_interest_metrics.py │ │ ├── 0017_virtual_report_types.py │ │ ├── 0018_virtual_report_types_translation.py │ │ ├── 0019_virtualreporttype_primary_dimension.py │ │ ├── 0020_materialized_interest_support.py │ │ ├── 0021_virtual_report_type_to_report_data_view.py │ │ ├── 0022_move_some_models_to_charts.py │ │ ├── 0023_importbatch_interest_processed.py │ │ ├── 0024_no_reportinterestmetric_name.py │ │ ├── 0025_modification_dates.py │ │ ├── 0026_interest_group_ordering.py │ │ ├── 0027_importbatch_interest_timestamp.py │ │ ├── 0028_reporttype_superseeded_by.py │ │ ├── 0029_remove_importbatch_interest_processed.py │ │ ├── 0030_manualdataupload_owner_level.py │ │ ├── 0031_add_accesslog_brin_indexes.py │ │ ├── 0032_optimize_accesslog_indexes.py │ │ ├── 0033_add_reportmaterializationspec.py │ │ ├── 0034_importbatch_materialization_info.py │ │ ├── 0035_reporttype_default_platform_interest.py │ │ ├── 0036_datasource_set_null.py │ │ ├── 0037_jsonfield.py │ │ ├── 0038_flexiblereport.py │ │ ├── 0038_reporttype_materialization_date.py │ │ ├── 0039_dimension_constraints.py │ │ ├── 0040_reporttype_approx_record_count.py │ │ ├── 0041_merge_20210327_1013.py │ │ ├── 0042_flexiblereport_last_updated_by.py │ │ ├── 0043_flexireport_accesslevel_ownership.py │ │ ├── 0044_better_source_related_constraints.py │ │ ├── 0045_dimension_int_to_str.py │ │ ├── 0046_one_import_batch_per_month.py │ │ ├── 0047_importbatch_last_updated_clickhoused.py │ │ ├── 0048_importbatchsynclog.py │ │ ├── 0049_mdu_many_importbatches.py │ │ ├── 0050_import_batch_date_remove_import_batch_from_mdu.py │ │ ├── 0051_split_import_batches.py │ │ ├── 0052_remove_importbatch_system_created.py │ │ ├── 0053_clickhouse_add_import_batch_idx.py │ │ ├── 0054_fill_importbatch_date.py │ │ ├── 0055_accesslog_logs_access_platfor_cdb3c4_idx.py │ │ ├── 0056_importbatch_logs_import_date_brin.py │ │ ├── 0057_rename_extra_manualdataupload_preflight.py │ │ ├── 0057_verbose_names.py │ │ ├── 0058_manualdataupload_error.py │ │ ├── 0059_manualdataupload_state.py │ │ ├── 0060_controlled_metrics.py │ │ ├── 0061_remove_manualdataupload_is_processed.py │ │ ├── 0062_merge_20220325_1628.py │ │ ├── 0063_alter_reportinterestmetric_interest_group.py │ │ ├── 0064_manualdataupload_error_details.py │ │ ├── 0065_remove_dimension_type.py │ │ ├── 0066_lastaction.py │ │ ├── 0067_alter_importbatchsynclog_state.py │ │ ├── 0067_mdu_sourcefile_mixin.py │ │ ├── 0068_file_checksums.py │ │ ├── 0069_merge_20220603_1325.py │ │ ├── 0070_reporttype_ext_id.py │ │ ├── 0071_mdu_import_batch_ordering.py │ │ ├── 0072_nibbler.py │ │ ├── 0073_alter_reportinterestmetric_unique_together.py │ │ ├── 0074_mdu_method.py │ │ ├── 0075_multimedia_interest.py │ │ ├── 0076_deduplicate_metrics.py │ │ ├── 0077_remove_source_from_dimension.py │ │ ├── 0078_remove_organizationplatform_sushi_credentials.py │ │ ├── 0079_alter_importbatch_unique_together.py │ │ ├── 0080_alter_organizationplatform_unique_together.py │ │ ├── 0081_mdu_extra_and_constraints.py │ │ ├── 0082_importbatch_manual_empty.py │ │ ├── 0083_alter_importbatchsynclog_last_updated_by_and_more.py │ │ ├── 0084_accesslog_item_and_dim8.py │ │ ├── 0085_interestgroup_implies_availability.py │ │ ├── 0086_flexiblereport_created_by_and_more.py │ │ ├── 0087_remove_reporttype_superseeded_by_and_more.py │ │ └── __init__.py │ ├── models.py │ ├── permissions.py │ ├── serializers.py │ ├── signals.py │ ├── static │ │ └── css │ │ │ └── report_type.css │ ├── tasks.py │ ├── tests │ │ ├── __init__.py │ │ ├── conftest.py │ │ ├── data │ │ ├── test_api.py │ │ ├── test_api_export_counter.py │ │ ├── test_api_flexiblereport.py │ │ ├── test_api_flexibleslicer.py │ │ ├── test_api_importbatch.py │ │ ├── test_api_manualdataupload.py │ │ ├── test_attempt_import.py │ │ ├── test_check_materialized_report_types.py │ │ ├── test_check_report_type_dimensions.py │ │ ├── test_cleanup.py │ │ ├── test_clickhouse.py │ │ ├── test_custom_import.py │ │ ├── test_data_deleting.py │ │ ├── test_data_import.py │ │ ├── test_export_analytical.py │ │ ├── test_export_counter.py │ │ ├── test_export_utils.py │ │ ├── test_fetch_intention_splitting_migration.py │ │ ├── test_flexibledataslicer.py │ │ ├── test_interest_calculation.py │ │ ├── test_last_action.py │ │ ├── test_manualdataupload_authorization.py │ │ ├── test_materialized_reports.py │ │ ├── test_migrations.py │ │ ├── test_models.py │ │ ├── test_models_manualdataupload.py │ │ └── test_title_reimport.py │ ├── translation.py │ ├── urls.py │ └── views.py ├── necronomicon │ ├── __init__.py │ ├── admin.py │ ├── apps.py │ ├── fake_data.py │ ├── migrations │ │ ├── 0001_initial.py │ │ ├── 0002_drop_fk_for_task_result.py │ │ ├── 0003_alter_batch_status_and_add_prepared_deleted.py │ │ └── __init__.py │ ├── models.py │ ├── tasks.py │ ├── templates │ │ └── necronomicon │ │ │ └── admin │ │ │ └── change_form.html │ └── tests │ │ ├── test_models.py │ │ └── test_tasks.py ├── nibbler │ ├── __init__.py │ ├── admin.py │ ├── apps.py │ ├── fake_data.py │ ├── logic │ │ ├── __init__.py │ │ ├── dict_reader.py │ │ ├── processing.py │ │ └── utils.py │ ├── management │ │ ├── __init__.py │ │ └── commands │ │ │ ├── __init__.py │ │ │ └── nigiri_vs_nibbler.py │ ├── migrations │ │ ├── 0001_initial.py │ │ ├── 0002_parserdefinition_platforms.py │ │ └── __init__.py │ └── models.py ├── organizations │ ├── __init__.py │ ├── admin.py │ ├── apps.py │ ├── fake_data.py │ ├── logic │ │ ├── __init__.py │ │ ├── queries.py │ │ └── sync.py │ ├── management │ │ ├── __init__.py │ │ └── commands │ │ │ ├── __init__.py │ │ │ ├── create_organization_api_keys.py │ │ │ ├── create_organizations_from_csv.py │ │ │ ├── delete_organization.py │ │ │ ├── erms_sync_organizations.py │ │ │ ├── load_sushi_credentials.py │ │ │ └── load_sushi_credentials_from_xlsx.py │ ├── migrations │ │ ├── 0001_initial.py │ │ ├── 0002_nullable_internal_id.py │ │ ├── 0003_non_unique_ico.py │ │ ├── 0004_organization_source.py │ │ ├── 0005_organization_users.py │ │ ├── 0006_organization_platforms.py │ │ ├── 0007_sushicredentials.py │ │ ├── 0008_sushicredentials_enabled.py │ │ ├── 0009_sushi_credentials_tuning.py │ │ ├── 0010_delete_sushicredentials.py │ │ ├── 0011_userorganization_source.py │ │ ├── 0012_timestamps.py │ │ ├── 0013_auto_20191113_0939.py │ │ ├── 0014_nullable_ico.py │ │ ├── 0015_nullable_ext_id.py │ │ ├── 0016_more_blank_fields.py │ │ ├── 0017_add_organizationaltname.py │ │ ├── 0018_datasource_set_null.py │ │ ├── 0019_jsonfield.py │ │ ├── 0020_organization_unique_shortname.py │ │ ├── 0021_alter_organization_options.py │ │ ├── 0022_organization_raw_enabled.py │ │ ├── 0023_alter_organizationaltname_options.py │ │ ├── 0024_alter_userorganization_unique_together.py │ │ └── __init__.py │ ├── models.py │ ├── serializers.py │ ├── tasks.py │ ├── tests │ │ ├── __init__.py │ │ ├── conftest.py │ │ ├── test_api.py │ │ ├── test_db.py │ │ ├── test_logic_sync.py │ │ └── test_models.py │ ├── translation.py │ ├── urls.py │ └── views.py ├── publications │ ├── __init__.py │ ├── admin.py │ ├── apps.py │ ├── fake_data.py │ ├── filters.py │ ├── logic │ │ ├── __init__.py │ │ ├── arrival_probabilities.py │ │ ├── cleanup.py │ │ ├── item_management.py │ │ ├── knowledgebase.py │ │ ├── sync.py │ │ ├── title_list_overlap.py │ │ ├── title_management.py │ │ ├── use_cases.py │ │ └── validation.py │ ├── management │ │ ├── __init__.py │ │ └── commands │ │ │ ├── __init__.py │ │ │ ├── cleanup_obsolete_platformtitles.py │ │ │ ├── create_platforms_from_csv.py │ │ │ ├── erms_sync_platforms.py │ │ │ ├── export_platforms.py │ │ │ ├── get_arrival_probabilities.py │ │ │ ├── load_registry_ids_from_csv.py │ │ │ ├── merge_titles.py │ │ │ ├── normalize_titles.py │ │ │ ├── remove_unused_titles.py │ │ │ ├── sync_pub_types_with_accesslogs.py │ │ │ └── update_arrival_curves.py │ ├── migrations │ │ ├── 0001_initial.py │ │ ├── 0002_title_doi.py │ │ ├── 0003_nullable_title_attrs.py │ │ ├── 0004_pub_type_verbose_name.py │ │ ├── 0005_auto_20190801_1615.py │ │ ├── 0006_no_null_title_text_attrs.py │ │ ├── 0007_title_unique_together.py │ │ ├── 0008_more_pub_types.py │ │ ├── 0009_even_more_pub_types.py │ │ ├── 0010_platform_interest_reports.py │ │ ├── 0011_unlocalize_short_name.py │ │ ├── 0012_platform_source.py │ │ ├── 0013_platforminterestreport_m2m.py │ │ ├── 0014_remove_old_interest_reports_attr.py │ │ ├── 0015_platformtitle.py │ │ ├── 0016_title_trgm_index.py │ │ ├── 0017_platformtitle_unique_data.py │ │ ├── 0018_platformtitle_unique_constraint.py │ │ ├── 0019_more_pub_types.py │ │ ├── 0020_platform_nullable_ext_id.py │ │ ├── 0021_platform_knowledgebase.py │ │ ├── 0022_datasource_set_null.py │ │ ├── 0023_jsonfield.py │ │ ├── 0024_better_source_related_constraints.py │ │ ├── 0025_platform_unique_shortname.py │ │ ├── 0026_title_type_data_migration.py │ │ ├── 0027_fill_platform_name.py │ │ ├── 0028_title_uris_proprietary_ids.py │ │ ├── 0028_verbose_names.py │ │ ├── 0029_title_default_pub_type.py │ │ ├── 0030_merge_0028_verbose_names_0029_title_default_pub_type.py │ │ ├── 0031_alter_title_unique_together.py │ │ ├── 0032_counter_registry_id.py │ │ ├── 0033_platform_duplicates.py │ │ ├── 0034_issn_renormalization.py │ │ ├── 0035_alter_platform_source.py │ │ ├── 0036_titleoverlapbatch.py │ │ ├── 0037_add_ir_m1_interest_to_all_platforms.py │ │ ├── 0038_issn_eissn_index.py │ │ ├── 0039_alter_titleoverlapbatch_last_updated_by.py │ │ ├── 0040_alter_title_unique_together.py │ │ ├── 0041_item.py │ │ ├── 0042_platform_sushi_arrival_stats.py │ │ ├── 0043_remove_platform_ext_id_source_null_and_more.py │ │ ├── 0044_alter_authortoitem_options_author_created_and_more.py │ │ └── __init__.py │ ├── models.py │ ├── serializers.py │ ├── tasks.py │ ├── tests │ │ ├── __init__.py │ │ ├── conftest.py │ │ ├── test_api.py │ │ ├── test_cleanup.py │ │ ├── test_commands.py │ │ ├── test_item_manager.py │ │ ├── test_merge_titles.py │ │ ├── test_models.py │ │ ├── test_sync.py │ │ ├── test_tasks.py │ │ ├── test_title_data_validation.py │ │ ├── test_title_list_overlap.py │ │ └── test_title_manager.py │ ├── translation.py │ ├── urls.py │ └── views.py ├── recache │ ├── __init__.py │ ├── admin.py │ ├── apps.py │ ├── management │ │ ├── __init__.py │ │ └── commands │ │ │ ├── __init__.py │ │ │ └── refresh_recache_from_older_django.py │ ├── migrations │ │ ├── 0001_initial.py │ │ ├── 0002_add_stats.py │ │ ├── 0003_cachedquery_origin.py │ │ ├── 0004_unique_query_hash.py │ │ ├── 0005_remove_cachedquery_query_pickle.py │ │ ├── 0006_query_hash_unique_with_django_version.py │ │ ├── 0007_alter_cachedquery_lifetime.py │ │ ├── 0008_remove_obsolete_caches.py │ │ └── __init__.py │ ├── models.py │ ├── tasks.py │ ├── tests │ │ ├── __init__.py │ │ ├── test_models.py │ │ └── test_util.py │ ├── util.py │ └── views.py ├── releases │ ├── __init__.py │ ├── apps.py │ ├── logic │ │ ├── __init__.py │ │ ├── changelog.py │ │ └── releases.py │ ├── migrations │ │ └── __init__.py │ ├── serializers.py │ ├── tests │ │ ├── __init__.py │ │ ├── conftest.py │ │ ├── test_api.py │ │ └── test_parsers.py │ ├── urls.py │ └── views.py ├── reporting │ ├── __init__.py │ ├── admin.py │ ├── apps.py │ ├── conftest.py │ ├── logic │ │ ├── __init__.py │ │ ├── computation.py │ │ ├── export.py │ │ ├── parsing.py │ │ └── report_definitions.py │ ├── models.py │ ├── serializers.py │ ├── tests │ │ ├── __init__.py │ │ ├── test_api.py │ │ ├── test_parsing.py │ │ └── test_platform_reports.py │ ├── urls.py │ └── views.py ├── scheduler │ ├── __init__.py │ ├── admin.py │ ├── apps.py │ ├── fake_data.py │ ├── filters │ │ ├── __init__.py │ │ ├── harvests.py │ │ └── intentions.py │ ├── logic │ │ ├── __init__.py │ │ └── automatic.py │ ├── migrations │ │ ├── 0001_initial.py │ │ ├── 0002_fetchintention_duplicit_of.py │ │ ├── 0003_harvest.py │ │ ├── 0004_automatic.py │ │ ├── 0005_fetchintention_date_constraint.py │ │ ├── 0006_fetchintention_retry_id.py │ │ ├── 0007_fetchintention_one_to_one_attempt.py │ │ ├── 0008_current_task.py │ │ ├── 0009_rename_retry_id_to_queue_id.py │ │ ├── 0010_fetchintention_canceled.py │ │ ├── 0011_fetchintention_previous.py │ │ ├── 0012_fill_queue_id.py │ │ ├── 0013_fetch_intention_queue.py │ │ ├── 0014_finalizing_import_batch.py │ │ ├── 0015_fetchintention_timestamp.py │ │ ├── 0016_fix_fi_queues.py │ │ ├── 0017_alter_scheduler_cooldown.py │ │ ├── 0018_alter_harvest_last_updated_by.py │ │ ├── 0019_alter_scheduler_too_many_requests_delay.py │ │ └── __init__.py │ ├── models.py │ ├── serializers.py │ ├── signals.py │ ├── tasks.py │ ├── tests │ │ ├── __init__.py │ │ ├── conftest.py │ │ ├── test_api.py │ │ └── test_models.py │ ├── urls.py │ └── views.py ├── sushi │ ├── __init__.py │ ├── admin.py │ ├── apps.py │ ├── fake_data.py │ ├── logic │ │ ├── __init__.py │ │ ├── cleanup.py │ │ ├── data_import.py │ │ └── export.py │ ├── management │ │ ├── __init__.py │ │ └── commands │ │ │ ├── __init__.py │ │ │ ├── assign_reports_to_credentials.py │ │ │ ├── change_credentials_url.py │ │ │ ├── check_reimport.py │ │ │ ├── cleanup_fetch_attempts.py │ │ │ ├── create_fake_sushi_for_demo.py │ │ │ ├── credentials_with_mismatched_url.py │ │ │ ├── find_missing_attempt_files.py │ │ │ ├── load_platform_report_table.py │ │ │ ├── remove_orphaned_files.py │ │ │ └── validate_sushi_json.py │ ├── migrations │ │ ├── 0001_initial.py │ │ ├── 0002_sushifetchattempt.py │ │ ├── 0003_counterreporttype_active.py │ │ ├── 0004_processing_info.py │ │ ├── 0005_attemp_queuing.py │ │ ├── 0006_sushicredentials_active_counter_reports.py │ │ ├── 0007_sushifetchattempt_import_batch.py │ │ ├── 0008_sushiattemp_importbatch_onetoone.py │ │ ├── 0009_conterreporttype_oneonone_report_type.py │ │ ├── 0010_counterreporttype_code_choices.py │ │ ├── 0011_sushifetchattempt_error_code.py │ │ ├── 0012_sushifetchattempt_contains_data.py │ │ ├── 0013_auto_20190829_1105.py │ │ ├── 0014_sushifetchattempt_processing_success.py │ │ ├── 0015_help_text.py │ │ ├── 0016_counterreporttype_superseeded_by.py │ │ ├── 0017_sushifetchattempt_processing_info.py │ │ ├── 0018_sushifetchattempt_in_progress.py │ │ ├── 0019_add_defaults_to_sushifetchattempt_booleans.py │ │ ├── 0020_remove_JR5_counter_report.py │ │ ├── 0021_sushifetchattempt_import_crashed.py │ │ ├── 0022_sushi_credentials_locking.py │ │ ├── 0023_sushicredentials_last_updated_by.py │ │ ├── 0024_lock_level_change.py │ │ ├── 0025_remove_counterreporttype_superseeded_by.py │ │ ├── 0026_customer_id_requestor_id_blank_fix.py │ │ ├── 0027_queue_previous_reverse_name.py │ │ ├── 0028_sushifetchattempt_credentials_version_hash.py │ │ ├── 0029_sushicredentials_version_hash.py │ │ ├── 0030_sushicredentials_title.py │ │ ├── 0031_sushifetchattempt_last_updated.py │ │ ├── 0032_unschedule_3030.py │ │ ├── 0033_sushifetchattempt_queue_id.py │ │ ├── 0034_longer_data_file_field.py │ │ ├── 0035_sushifetchattempt_http_status_code.py │ │ ├── 0036_credentials_intermediate_report_types.py │ │ ├── 0037_broken_credentials.py │ │ ├── 0038_jsonfield.py │ │ ├── 0039_remove_http_auth_from_c5.py │ │ ├── 0040_broken_blank_true.py │ │ ├── 0041_more_report_types.py │ │ ├── 0042_counterreporttype_code_update.py │ │ ├── 0043_sushifetchattempt_partial_data.py │ │ ├── 0044_fix_data_already_imported.py │ │ ├── 0045_fetchattempt_statemachine.py │ │ ├── 0046_fetchintentions_for_fetch_attempts.py │ │ ├── 0047_no_data_for_3031_and_3030.py │ │ ├── 0048_discard_credentials_broken_state.py │ │ ├── 0049_sushifetchattempt_extracted_data.py │ │ ├── 0050_alter_sushicredentials_api_key.py │ │ ├── 0051_fetchattempt_remove_queue_stuff.py │ │ ├── 0052_select_best_empty_importbatch.py │ │ ├── 0053_sourcefile_mixin.py │ │ ├── 0054_file_checksums.py │ │ ├── 0055_fill_missing_extracted_data.py │ │ ├── 0056_alter_sushifetchattempt_credentials.py │ │ ├── 0057_alter_sushifetchattempt_status.py │ │ ├── 0058_help_text_fix.py │ │ ├── 0059_credentials_last_harvestable_month.py │ │ ├── 0060_alter_sushicredentials_last_updated_by.py │ │ ├── 0061_sushicredentials_auto_update_url_and_more.py │ │ ├── 0062_sushifetchattempt_clashing_import_batch.py │ │ ├── 0063_alter_counterreporttype_code.py │ │ ├── 0064_sushifetchattempt_sushi_url.py │ │ └── __init__.py │ ├── models.py │ ├── serializers.py │ ├── static │ │ └── admin │ │ │ └── admin.css │ ├── tasks.py │ ├── templates │ │ ├── Template_for_SushiCredentials_import_consortium.xlsx │ │ └── Template_for_SushiCredentials_import_singleorg.xlsx │ ├── tests │ │ ├── __init__.py │ │ ├── conftest.py │ │ ├── data │ │ ├── test_api.py │ │ ├── test_arrival_probabilities.py │ │ ├── test_commands.py │ │ ├── test_fetching.py │ │ ├── test_logic_data_import.py │ │ ├── test_sushi_fetching.py │ │ ├── test_sushicredentials_model.py │ │ ├── test_sushifetchattempt_model.py │ │ ├── test_tasks.py │ │ └── test_views.py │ ├── urls.py │ └── views.py └── tags │ ├── __init__.py │ ├── admin.py │ ├── apps.py │ ├── fake_data.py │ ├── filters.py │ ├── logic │ ├── __init__.py │ └── titles_lists.py │ ├── management │ ├── __init__.py │ └── commands │ │ ├── __init__.py │ │ ├── create_fake_title_tags.py │ │ ├── tag_title_list.py │ │ └── test_tag_performance.py │ ├── migrations │ ├── 0001_initial.py │ ├── 0002_tagclass_defaults_for_tag.py │ ├── 0003_tagging_batch.py │ ├── 0004_alter_taggingbatch_state.py │ ├── 0005_alter_tag_options.py │ ├── 0006_tagging_batch_cascade.py │ ├── 0007_tagging_batch_internal_name.py │ ├── 0008_alter_taggingbatch_source_file.py │ ├── 0009_usertagclass.py │ ├── 0010_tagging_attempts_and_more.py │ ├── 0011_recreate_tag_constraints.py │ ├── 0012_alter_organizationtag_last_updated_by_and_more.py │ └── __init__.py │ ├── models.py │ ├── permissions.py │ ├── serializers.py │ ├── tasks.py │ ├── tests │ ├── __init__.py │ ├── test_batch_tagging.py │ ├── test_logic_title_lists.py │ ├── test_models.py │ └── test_views.py │ ├── urls.py │ └── views.py ├── bootstrap.sh ├── ci └── Dockerfile ├── config ├── __init__.py ├── celery_conf.py ├── permissions.py ├── production.wsgi ├── settings │ ├── __init__.py │ ├── base.py │ ├── devel.py │ ├── docker.py │ ├── production.py │ ├── staging.py │ └── test.py ├── staging.wsgi ├── urls.py └── wsgi.py ├── create_fixtures.sh ├── data ├── TestFlexibleDataSlicer.csv └── initial-data.json ├── design └── ui │ ├── .babelrc │ ├── .env │ ├── .gitignore │ ├── README.md │ ├── babel.config.js │ ├── jest.config.js │ ├── package.json │ ├── public │ ├── favicon.ico │ ├── favicon.png │ ├── index.html │ └── maintenance.html │ ├── specs │ ├── CheckMark.spec.js │ ├── SushiCredentialsEditDialog.spec.js-disabled │ ├── dates.spec.js │ ├── fetch-intention-states.spec.js │ └── sushi-validation.spec.js │ ├── src │ ├── assets │ │ ├── MSMT_cz.svg │ │ ├── MSMT_cz_opt.svg │ │ ├── celus-dark.png │ │ ├── celus-horizontal-dark.svg │ │ ├── celus-one-v1-dark.svg │ │ ├── celus-one-v3-dark.svg │ │ ├── celus-plus-dark.svg │ │ ├── celus-plus-white-vertical-nobg.svg │ │ ├── czechelib-stats-64.svg │ │ ├── czechelib-stats.svg │ │ ├── czechelib.svg │ │ ├── czechelib_logo-mini_color-transp.png │ │ ├── eu-96.png │ │ ├── eu-large.png │ │ ├── eu.png │ │ ├── eu_fond.svg │ │ ├── eu_fond_plain.svg │ │ ├── ex-metric.png │ │ ├── ex-title-metric-publisher-success.png │ │ ├── ex-title.png │ │ ├── hungry-celus-240.png │ │ ├── hungry-celus.png │ │ ├── ntk-96.svg │ │ ├── sushi_credentials-enable.png │ │ ├── sushi_credentials-test.png │ │ └── sushi_credentials.png │ ├── components │ │ ├── AccessLogList.vue │ │ ├── AddAnnotationButton.vue │ │ ├── AddPlatformButton.vue │ │ ├── AnnotationCreateModifyWidget.vue │ │ ├── AnnotationList.vue │ │ ├── AnnotationsWidget.vue │ │ ├── BootUpWidget.vue │ │ ├── CancelSimulationWidget.vue │ │ ├── ChartDataTable.vue │ │ ├── ChartTypeSelector.vue │ │ ├── CounterDataExportWidget.vue │ │ ├── CustomUploadInfoWidget.vue │ │ ├── DataExportWidget.vue │ │ ├── DateRangeSelector.vue │ │ ├── EditPriceDialog.vue │ │ ├── HarvestsTable.vue │ │ ├── HoverText.vue │ │ ├── ImportBatchChart.vue │ │ ├── ImportBatchesDeleteConfirm.vue │ │ ├── ImportBatchesList.vue │ │ ├── ImportPreflightDataWidget.vue │ │ ├── InterestOverviewMetrics.vue │ │ ├── InterestOverviewReports.vue │ │ ├── MDUChart.vue │ │ ├── ManualUploadButton.vue │ │ ├── ManualUploadListTable.vue │ │ ├── ManualUploadState.vue │ │ ├── NewCelusVersionDialog.vue │ │ ├── OrganizationList.vue │ │ ├── OrganizationPlatformInterestHeatmap.vue │ │ ├── OverlapPyramidWidget.vue │ │ ├── PlatformCostList.vue │ │ ├── PlatformEditDialog.vue │ │ ├── PlatformInterestChart.vue │ │ ├── PlatformList.vue │ │ ├── PlatformOverlapTable.vue │ │ ├── PlatformVsAllOverlapTable.vue │ │ ├── RawDataExportWidget.vue │ │ ├── ReleaseCard.vue │ │ ├── ReportInterestGroups.vue │ │ ├── ReportTypeExamples.vue │ │ ├── ReportTypeInfoWidget.vue │ │ ├── SelectedDateRangeWidget.vue │ │ ├── ShortenText.vue │ │ ├── SupportedNonCounterPlatformsList.vue │ │ ├── TitleList.vue │ │ ├── TitleTypeFilterWidget.vue │ │ ├── TopTenDashboardWidget.vue │ │ ├── UndefinedInterestWidget.vue │ │ ├── account │ │ │ ├── AccountCreateModifyWidget.vue │ │ │ ├── CreateOrganizationDialog.vue │ │ │ ├── LoginDialog.vue │ │ │ ├── OtpDialog.vue │ │ │ └── PasswordChangeDialog.vue │ │ ├── admin │ │ │ ├── DeletePlatformDataWidget.vue │ │ │ ├── ManagementCommand.vue │ │ │ ├── ManagementCommandList.vue │ │ │ └── ReportViewToChartManagementWidget.vue │ │ ├── charts │ │ │ ├── APIChart.vue │ │ │ ├── CounterChartSet.vue │ │ │ ├── CoverageMap.vue │ │ │ ├── CoverageOverviewWidget.vue │ │ │ ├── CoverageScoreGauge.vue │ │ │ ├── SushiArrivalCurve.vue │ │ │ └── SushiStatusChart.vue │ │ ├── coverage │ │ │ └── CoverageCard.vue │ │ ├── events │ │ │ ├── EventCategoryMark.vue │ │ │ ├── EventCategorySelect.vue │ │ │ ├── EventImportanceIcon.vue │ │ │ ├── EventImportanceSelect.vue │ │ │ └── EventList.vue │ │ ├── items │ │ │ └── ItemList.vue │ │ ├── overlap │ │ │ ├── TitleOverlapBatchList.vue │ │ │ └── TitleOverlapUploadFileDialog.vue │ │ ├── reporting │ │ │ ├── AccessLevelSelector.vue │ │ │ ├── CopyReportDialog.vue │ │ │ ├── ExportOverviewTable.vue │ │ │ ├── FilterCard.vue │ │ │ ├── FilterSpec.vue │ │ │ ├── FlexiTableEditor.vue │ │ │ ├── FlexiTableOutput.vue │ │ │ ├── MetricChip.vue │ │ │ ├── ReportChip.vue │ │ │ ├── ReportLoadingWidget.vue │ │ │ ├── ReportNamingWidget.vue │ │ │ ├── ReportSpecOverview.vue │ │ │ ├── ReportingChart.vue │ │ │ ├── StoredReportsTable.vue │ │ │ └── TrendArrow.vue │ │ ├── selectors │ │ │ ├── DimensionKeySelector.vue │ │ │ ├── InterestGroupSelector.vue │ │ │ ├── OrganizationSelectionWidget.vue │ │ │ ├── OrganizationSelector.vue │ │ │ ├── PlatformSelectionWidget.vue │ │ │ ├── PlatformSelector.vue │ │ │ └── ReportViewSelector.vue │ │ ├── special │ │ │ ├── ReportPartParams.vue │ │ │ ├── SpecializedReport.vue │ │ │ └── SpecializedReportPart.vue │ │ ├── sushi │ │ │ ├── AttemptExtractedData.vue │ │ │ ├── CounterReportLastHarvestableMonthWidget.vue │ │ │ ├── DeleteSushiCredentialsDataWidget.vue │ │ │ ├── FetchAttemptModeFilter.vue │ │ │ ├── FetchIntentionStatusIcon.vue │ │ │ ├── HarvestSelectedWidget.vue │ │ │ ├── HarvesterIPAddressList.vue │ │ │ ├── IconButton.vue │ │ │ ├── IntroVideo.vue │ │ │ ├── LastHarvestableMonthEntryWidget.vue │ │ │ ├── RegistryIcon.vue │ │ │ ├── SushiAttemptListWidget.vue │ │ │ ├── SushiCredentialsDataDialog.vue │ │ │ ├── SushiCredentialsEditDialog.vue │ │ │ ├── SushiCredentialsManagementWidget.vue │ │ │ ├── SushiCredentialsMonthOverviewWidget.vue │ │ │ ├── SushiCredentialsOverviewHeaderWidget.vue │ │ │ ├── SushiFetchIntentionStateIcon.vue │ │ │ ├── SushiFetchIntentionsListWidget.vue │ │ │ ├── SushiHarvestedSlotsWidget.vue │ │ │ ├── SushiMarkAsEmptyWidget.vue │ │ │ ├── SushiMonthStatusIcon.vue │ │ │ ├── SushiReportIndicator.vue │ │ │ └── SushiStatsDashboardWidget.vue │ │ ├── tagging-batches │ │ │ ├── TaggingAttemptList.vue │ │ │ ├── TaggingBatchList.vue │ │ │ ├── TaggingBatchProcessingWidget.vue │ │ │ ├── TaggingBatchStateWidget.vue │ │ │ └── TaggingBatchStats.vue │ │ ├── tags │ │ │ ├── AddTagButton.vue │ │ │ ├── AddTagClassButton.vue │ │ │ ├── EditTagClassWidget.vue │ │ │ ├── EditTagWidget.vue │ │ │ ├── TagAccessLevelSelector.vue │ │ │ ├── TagCard.vue │ │ │ ├── TagChip.vue │ │ │ ├── TagChipSimple.vue │ │ │ ├── TagClassScopeWidget.vue │ │ │ ├── TagClassSelector.vue │ │ │ ├── TagListWidget.vue │ │ │ └── TagSelector.vue │ │ ├── tasks │ │ │ └── ServerTaskMonitor.vue │ │ └── util │ │ │ ├── AsColumnCheckbox.vue │ │ │ ├── CheckMark.vue │ │ │ ├── ColorEntry.vue │ │ │ ├── CompositionBar.vue │ │ │ ├── DateRangeText.vue │ │ │ ├── DateWithTooltip.vue │ │ │ ├── DoiLink.vue │ │ │ ├── ErrorDialog.vue │ │ │ ├── ErrorPlaceholder.vue │ │ │ ├── ExportMonitorWidget.vue │ │ │ ├── FromToMonthEntry.vue │ │ │ ├── FromToYearEntry.vue │ │ │ ├── GoodBadMark.vue │ │ │ ├── ItemBadge.vue │ │ │ ├── LargeSpinner.vue │ │ │ ├── LoaderWidget.vue │ │ │ ├── MenuListItem.vue │ │ │ ├── MonthEntry.vue │ │ │ ├── SimpleCompare.vue │ │ │ ├── SimplePie.vue │ │ │ ├── SmallLoader.vue │ │ │ ├── TwoPaneSelector.vue │ │ │ └── YearEntry.vue │ ├── i18n.js │ ├── libs │ │ ├── attempt-state.js │ │ ├── charts.js │ │ ├── counter_header.js │ │ ├── curve.js │ │ ├── data-state.js │ │ ├── dates.js │ │ ├── db-object-localization.js │ │ ├── dimensions.js │ │ ├── email-validation.js │ │ ├── flexi-reports.js │ │ ├── group-ids.js │ │ ├── http.js │ │ ├── id-translation.js │ │ ├── intention-state.js │ │ ├── interest.js │ │ ├── numbers.js │ │ ├── organizations.js │ │ ├── palettes.js │ │ ├── pivot.js │ │ ├── pub-types.js │ │ ├── reporting-interface.js │ │ ├── serialization.js │ │ ├── server-task.js │ │ ├── sleep.js │ │ ├── sorting.js │ │ ├── sources.js │ │ ├── strings.js │ │ ├── sushi-validation.js │ │ ├── tags.js │ │ ├── unique-mapping.js │ │ └── user.js │ ├── locales │ │ ├── annotations.yaml │ │ ├── charts.yaml │ │ ├── common.yaml │ │ ├── dialog.yaml │ │ ├── en.json │ │ ├── errors.yaml │ │ ├── notifications.yaml │ │ ├── pub-types.yaml │ │ ├── reporting.yaml │ │ ├── sources.yaml │ │ └── sushi.yaml │ ├── main.js │ ├── mixins │ │ ├── cancellation.js │ │ ├── dimensions.js │ │ ├── formRulesMixin.js │ │ ├── reportTypes.js │ │ ├── stateTracking.js │ │ ├── tagAccessLevels.js │ │ ├── tagColors.js │ │ ├── tags.js │ │ └── translators.js │ ├── pages │ │ ├── AccountManagementPage.vue │ │ ├── AllTitlesListPage.vue │ │ ├── AnnotationListPage.vue │ │ ├── ChangeLogPage.vue │ │ ├── CustomDataUploadPage.vue │ │ ├── DashboardPage.vue │ │ ├── DataCoverageOverviewPage.vue │ │ ├── EmailNotVerified.vue │ │ ├── EventsPage.vue │ │ ├── ExportOverviewPage.vue │ │ ├── FlexiTablePage.vue │ │ ├── FlexibleReportsPage.vue │ │ ├── HarvestsPage.vue │ │ ├── InterestOverviewPage.vue │ │ ├── InvalidUserPage.vue │ │ ├── ItemDetailPage.vue │ │ ├── Main.vue │ │ ├── MaintenancePage.vue │ │ ├── ManagementCommandsPage.vue │ │ ├── ManagementPage.vue │ │ ├── ManualUploadListPage.vue │ │ ├── NotFoundPage.vue │ │ ├── OrganizationListPage.vue │ │ ├── OrganizationPlatformInterestOverviewPage.vue │ │ ├── OverallCoverageDashboardWidget.vue │ │ ├── OverlapAnalysisPage.vue │ │ ├── PasswordResetPage.vue │ │ ├── PlatformDetailPage.vue │ │ ├── PlatformListPage.vue │ │ ├── PlatformOverlapAnalysisPage.vue │ │ ├── PlatformOverviewWidget.vue │ │ ├── ReleasesPage.vue │ │ ├── SidePanel.vue │ │ ├── SpecializedReportsPage.vue │ │ ├── StandardLayout.vue │ │ ├── SupportedNonCounterPlatformsPage.vue │ │ ├── SushiCredentialsManagementPage.vue │ │ ├── SushiCredentialsMonthOverviewPage.vue │ │ ├── SushiTroubleshootingPage.vue │ │ ├── TagListPage.vue │ │ ├── TaggingBatchesPage.vue │ │ ├── TitleDetailPage.vue │ │ ├── TitleListOverlapPage.vue │ │ ├── UserPage.vue │ │ └── VerifyEmailPage.vue │ ├── plugins │ │ └── vuetify.js │ ├── router │ │ └── index.js │ ├── store │ │ ├── index.js │ │ └── modules │ │ │ ├── cancellation.js │ │ │ ├── events.js │ │ │ ├── interest.js │ │ │ ├── login.js │ │ │ ├── maintenance.js │ │ │ ├── page-settings.js │ │ │ └── site-config.js │ └── workers │ │ └── event-worker.js │ ├── vue.config.js │ └── yarn.lock ├── docker ├── docker-compose-k8s.yml ├── docker-compose.yml ├── entrypoint-celery-beat.sh ├── entrypoint-celery.py ├── entrypoint-web.sh ├── env.list └── nginx.conf ├── docs ├── Makefile ├── Uživatelská dokumentace.odt ├── admin.rst ├── advanced-topics.rst ├── ansible │ ├── celus.yaml │ ├── host_vars │ │ └── example.com │ │ │ └── vars │ └── roles │ │ └── celus │ │ ├── defaults │ │ └── main.yml │ │ ├── handlers │ │ └── main.yaml │ │ ├── tasks │ │ ├── celery.yaml │ │ ├── django.yaml │ │ ├── main.yaml │ │ └── postgres.yaml │ │ └── templates │ │ ├── etc_confd_celery.j2 │ │ ├── etc_confd_celerybeat.j2 │ │ ├── etc_cron_daily_backup_db.j2 │ │ ├── etc_cron_daily_cleanup_django_sessions.j2 │ │ ├── etc_logrotated_celery.j2 │ │ ├── etc_profiled_virtualenvwrapper.sh.j2 │ │ ├── etc_systemd_system_celery.service.j2 │ │ ├── etc_systemd_system_celerybeat.service.j2 │ │ ├── root_activate_virtualenv.sh.j2 │ │ └── secret_settings.json.j2 ├── conf.py ├── examples │ ├── ex-just-values.csv │ ├── ex-just-values.ods │ ├── ex-metric.csv │ ├── ex-metric.ods │ ├── ex-metric.png │ ├── ex-title-metric-publisher-success.ods │ ├── ex-title-metric-publisher-success.png │ ├── ex-title-metric-publisher.ods │ ├── ex-title.ods │ └── ex-title.png ├── external-api.rst ├── images │ ├── api-key-create.png │ ├── api-key-get-value.png │ ├── dashboard.png │ ├── dja_add_chart_definition.png │ ├── dja_add_dimension.png │ ├── dja_add_report_data_view.png │ ├── dja_add_report_type.png │ ├── dja_add_report_type_filled.png │ ├── dja_add_report_view_to_chart_type.png │ ├── dja_report_data_view_tr_jr1.png │ ├── dja_sushi_app.png │ ├── dja_sushi_attempt_delete.png │ ├── sidebar_sushi_management.png │ ├── sushi_attempt_list.png │ ├── sushi_button_close.png │ ├── sushi_button_delete.png │ ├── sushi_button_save.png │ ├── sushi_button_save_and_test.png │ ├── sushi_create_dialog.png │ ├── sushi_credentials_table.png │ ├── sushi_edit_dialog.png │ ├── sushi_test_dialog_start.png │ ├── sushi_test_failure.png │ ├── sushi_test_running.png │ ├── sushi_test_running_cs.png │ ├── sushi_test_success.png │ ├── sushi_top_bar.png │ ├── tag-class-creation-5_0.png │ ├── tag-creation-5_0.png │ ├── tag-list-new-tag-5_0.png │ ├── tag-title-assign-2-5_0.png │ ├── tag-title-assign-5_0.png │ ├── tagging-batch-assign-5_0.png │ ├── tagging-batch-done-5_0.png │ ├── tagging-batch-example-5_0.png │ ├── tagging-batch-list-2-5_0.png │ ├── tagging-batch-list-3-5_0.png │ ├── tagging-batch-list-5_0.png │ ├── tagging-batch-preprocess-5_0.png │ ├── tagging-batch-upload-5_0.png │ ├── tags-management-empty-5_0.png │ ├── tags-reporting-filter-5_0.png │ ├── tags-reporting-grouping-1-5_0.png │ ├── tags-reporting-grouping-remainder-5_0.png │ ├── tags-title-filtering-1-5_0.png │ ├── tags-title-filtering-2-5_0.png │ └── title-tag-filtering-5_0.png ├── index.rst ├── install.rst ├── requirements.txt ├── server-maintenance.rst ├── tags.rst ├── topics.rst ├── trying-celus.rst └── user.rst ├── dump_basic_defs.sh ├── fix_csv.py ├── item-report-todo.rst ├── k8s ├── INSTALL.rst ├── celery-beat-deployment.yaml ├── celery-worker-deployment.yaml ├── generate.sh ├── ingress-config.yaml ├── letsencrypt-issuer.yaml ├── nginx-deployment.yaml ├── nginx-ingress.yaml ├── nginx-service.yaml ├── postgres-deployment.yaml ├── postgres-service.yaml ├── redis-deployment.yaml ├── redis-service.yaml ├── secrets │ ├── secrets-celus.yaml │ └── secrets-gitlab.yaml ├── web-deployment.yaml └── web-service.yaml ├── locale └── cs │ └── LC_MESSAGES │ └── django.po ├── manage.py ├── poetry.lock ├── pyproject.toml ├── pytest.ini ├── readthedocs.yaml ├── start_celery.sh ├── start_celerybeat.sh ├── test-data ├── counter4 │ ├── 4_BR2_invalid_location1.xml │ ├── 4_BR2_not_supported1.xml │ ├── 4_BR2_service_not_available1.xml │ ├── 4_BR2_unauthorized1.xml │ ├── 4_BR2_unauthorized2.xml │ ├── 4_JR1_missing_reports_tag.xml │ ├── 4_JR2_denials.tsv │ ├── 4_PR1_invalid_requestor.xml │ ├── counter4_br2.tsv │ ├── counter4_br2_one_month.tsv │ ├── counter4_jr1_empty.tsv │ ├── sushi_1111-severity-missing.xml │ ├── sushi_1111-severity-number.xml │ ├── sushi_1111.xml │ ├── sushi_3030.xml │ ├── sushi_3040.xml │ └── sushi_exception-with-extra-attrs.xml ├── counter5 │ ├── 5_DR_ProQuestEbookCentral_exception.json │ ├── 5_TR_ProQuestEbookCentral.json │ ├── 5_TR_ProQuestEbookCentral_exception.json │ ├── 5_TR_with_warning.json │ ├── C5_PR_test.json │ ├── C5_PR_with_3030.json │ ├── C5_PR_with_3040.json │ ├── COUNTER_R5_Report_Examples_DR.csv │ ├── COUNTER_R5_Report_Examples_PR.csv │ ├── COUNTER_R5_Report_Examples_TR.csv │ ├── TR-one-title-more-ids.json │ ├── TR-wrong-encoding.csv │ ├── code-zero.json │ ├── counter5_ir_sample.json │ ├── counter5_ir_sample.tsv │ ├── counter5_ir_sample_no_parents.json │ ├── counter5_table_dr.csv │ ├── counter5_table_dr.tsv │ ├── counter5_table_dr_empty.csv │ ├── counter5_table_ir_m1.csv │ ├── counter5_table_mismatched_rt.csv │ ├── counter5_table_pr.csv │ ├── counter5_table_pr_empty.csv │ ├── counter5_table_pr_wrong_value.csv │ ├── counter5_table_tr_empty.csv │ ├── counter5_tr_nature.json │ ├── counter5_tr_test1.json │ ├── counter5_tr_test1_wrong_id.json │ ├── data_incorrect.json │ ├── dr-extra-ids.json │ ├── error-in-root.json │ ├── invalid-customer.json │ ├── naked_error.json │ ├── naked_error_3000.json │ ├── naked_error_lowercase.json │ ├── naked_errors.json │ ├── no_data.json │ ├── no_data_3050.json │ ├── no_data_3062.json │ ├── no_json.txt │ ├── null-in-Item_ID.json │ ├── partial_data1.json │ ├── partial_data2.json │ ├── partial_data3.json │ ├── severity-missing.json │ ├── severity-number.json │ ├── severity-wrong.json │ ├── some_data_3050.json │ ├── some_data_3062.json │ └── stringified_error.json ├── custom │ ├── custom_data-2d-3x2x3-endate.csv │ ├── custom_data-2d-3x2x3-isodate.csv │ ├── custom_data-2d-3x2x3-org-isodate-single.csv │ ├── custom_data-2d-3x2x3-org-isodate.csv │ ├── custom_data-nibbler-simple.csv │ ├── custom_data-simple-3x3-endate.csv │ └── custom_data-simple-3x3-isodate.csv ├── releases │ ├── test_empty_releases.yaml │ └── test_filled_releases.yaml └── tagging_batch │ ├── plain-title-list-with-tags.csv │ ├── plain-title-list.csv │ ├── scopus-codes-head.csv │ ├── scopus-topics-test.csv │ └── simple-title-list-with-bom.csv ├── test_coverage.sh ├── test_scenarios ├── __init__.py ├── basic.py └── counter_data.py └── wsserver.py /.coveragerc: -------------------------------------------------------------------------------- 1 | [report] 2 | precision = 1 3 | show_missing = true 4 | skip_covered = true 5 | 6 | [run] 7 | omit = 8 | */migrations/* 9 | */management/* 10 | */tests.py 11 | */experiments.py 12 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | __pycache__ 2 | .idea/ 3 | *~ 4 | config/settings/secret_settings.json 5 | .cache 6 | .coverage 7 | locale/*/LC_MESSAGES/django.mo 8 | celerybeat-schedule 9 | /.pytest_cache 10 | .~* 11 | media/* 12 | /.env 13 | /venv 14 | error.log 15 | .DS_Store 16 | -------------------------------------------------------------------------------- /.gitlab/pre-commit.yml: -------------------------------------------------------------------------------- 1 | lints:pre_commit: 2 | stage: lints 3 | needs: [] 4 | image: registry.gitlab.com/big-dig-data/celus/celus-test:latest 5 | services: 6 | - postgres 7 | variables: 8 | DJANGO_SETTINGS_MODULE: config.settings.test 9 | POSTGRES_DB: celus 10 | POSTGRES_USER: celus 11 | POSTGRES_PASSWORD: celus 12 | POSTGRES_HOST: postgres 13 | before_script: 14 | - poetry install --no-root 15 | - cp .env.example .env 16 | script: 17 | - poetry run pre-commit run --hook-stage push --all-files 18 | tags: 19 | - bdd 20 | -------------------------------------------------------------------------------- /.gitlab/test-js.yml: -------------------------------------------------------------------------------- 1 | tests:test_js: 2 | stage: tests 3 | needs: [] 4 | image: node:18.19 5 | script: 6 | - cd design/ui 7 | - yarn install 8 | - yarn test 9 | tags: 10 | - bdd 11 | -------------------------------------------------------------------------------- /apps/activity/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techlib/celus/5ad1bd73a210cb5456eeea442913fa4f7649b7b5/apps/activity/__init__.py -------------------------------------------------------------------------------- /apps/activity/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | 3 | from .models import UserActivity 4 | 5 | 6 | @admin.register(UserActivity) 7 | class UserActivityAdmin(admin.ModelAdmin): 8 | list_display = ["user", "timestamp", "action_type"] 9 | list_filter = ["user"] 10 | search_fields = ["user__email", "user__username"] 11 | -------------------------------------------------------------------------------- /apps/activity/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class ActivityConfig(AppConfig): 5 | name = "activity" 6 | 7 | def ready(self): 8 | from . import signals # noqa - this is necessary to run the code in the module 9 | -------------------------------------------------------------------------------- /apps/activity/migrations/0002_useractivity_plural.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.13 on 2020-07-20 17:34 2 | 3 | from django.db import migrations 4 | 5 | 6 | class Migration(migrations.Migration): 7 | dependencies = [("activity", "0001_initial")] 8 | 9 | operations = [ 10 | migrations.AlterModelOptions( 11 | name="useractivity", options={"verbose_name_plural": "User activities"} 12 | ) 13 | ] 14 | -------------------------------------------------------------------------------- /apps/activity/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techlib/celus/5ad1bd73a210cb5456eeea442913fa4f7649b7b5/apps/activity/migrations/__init__.py -------------------------------------------------------------------------------- /apps/activity/models.py: -------------------------------------------------------------------------------- 1 | from django.conf import settings 2 | from django.db import models 3 | from django.utils.timezone import now 4 | 5 | 6 | class UserActivity(models.Model): 7 | ACTION_TYPE_LOGIN = "LGN" 8 | 9 | ACTION_TYPE_CHOICES = ((ACTION_TYPE_LOGIN, "Login"),) 10 | 11 | user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE) 12 | action_type = models.CharField(max_length=3, choices=ACTION_TYPE_CHOICES) 13 | timestamp = models.DateTimeField(default=now) 14 | 15 | class Meta: 16 | verbose_name_plural = "User activities" 17 | -------------------------------------------------------------------------------- /apps/activity/signals.py: -------------------------------------------------------------------------------- 1 | from django.contrib.auth import user_logged_in 2 | from django.dispatch import receiver 3 | 4 | from activity.models import UserActivity 5 | 6 | 7 | @receiver(user_logged_in) 8 | def log_user_activity_login(request, user, **kwargs): 9 | UserActivity.objects.create(user=user, action_type=UserActivity.ACTION_TYPE_LOGIN) 10 | -------------------------------------------------------------------------------- /apps/activity/tests.py: -------------------------------------------------------------------------------- 1 | # Create your tests here. 2 | -------------------------------------------------------------------------------- /apps/activity/views.py: -------------------------------------------------------------------------------- 1 | # Create your views here. 2 | -------------------------------------------------------------------------------- /apps/annotations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techlib/celus/5ad1bd73a210cb5456eeea442913fa4f7649b7b5/apps/annotations/__init__.py -------------------------------------------------------------------------------- /apps/annotations/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | from modeltranslation.admin import TranslationAdmin 3 | 4 | from .models import Annotation 5 | 6 | 7 | @admin.register(Annotation) 8 | class AnnotationAdmin(TranslationAdmin): 9 | list_display = [ 10 | "subject", 11 | "level", 12 | "start_date", 13 | "end_date", 14 | "organization", 15 | "platform", 16 | "author", 17 | ] 18 | search_fields = [ 19 | "subject", 20 | "short_message", 21 | "message", 22 | "organization__short_name", 23 | "platform__short_name", 24 | ] 25 | list_filter = ["level", "platform", "organization", "author"] 26 | -------------------------------------------------------------------------------- /apps/annotations/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class AnnotationsConfig(AppConfig): 5 | name = "annotations" 6 | -------------------------------------------------------------------------------- /apps/annotations/migrations/0002_no_title_no_report_type.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.4 on 2019-09-25 12:23 2 | 3 | from django.db import migrations 4 | 5 | 6 | class Migration(migrations.Migration): 7 | dependencies = [("annotations", "0001_initial")] 8 | 9 | operations = [ 10 | migrations.RemoveField(model_name="annotation", name="report_type"), 11 | migrations.RemoveField(model_name="annotation", name="title"), 12 | ] 13 | -------------------------------------------------------------------------------- /apps/annotations/migrations/0003_annotation_level.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.4 on 2019-09-25 12:28 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | dependencies = [("annotations", "0002_no_title_no_report_type")] 8 | 9 | operations = [ 10 | migrations.AddField( 11 | model_name="annotation", 12 | name="level", 13 | field=models.CharField( 14 | choices=[("info", "info"), ("important", "important")], 15 | default="info", 16 | max_length=20, 17 | ), 18 | ) 19 | ] 20 | -------------------------------------------------------------------------------- /apps/annotations/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techlib/celus/5ad1bd73a210cb5456eeea442913fa4f7649b7b5/apps/annotations/migrations/__init__.py -------------------------------------------------------------------------------- /apps/annotations/tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techlib/celus/5ad1bd73a210cb5456eeea442913fa4f7649b7b5/apps/annotations/tests/__init__.py -------------------------------------------------------------------------------- /apps/annotations/tests/conftest.py: -------------------------------------------------------------------------------- 1 | from core.tests.conftest import * # noqa - fixtures 2 | from organizations.tests.conftest import * # noqa - fixtures 3 | from publications.tests.conftest import * # noqa - fixtures 4 | -------------------------------------------------------------------------------- /apps/annotations/translation.py: -------------------------------------------------------------------------------- 1 | from modeltranslation.translator import TranslationOptions, translator 2 | 3 | from .models import Annotation 4 | 5 | 6 | class AnnotationTranslationOptions(TranslationOptions): 7 | fields = ("subject", "short_message", "message") 8 | 9 | 10 | translator.register(Annotation, AnnotationTranslationOptions) 11 | -------------------------------------------------------------------------------- /apps/annotations/urls.py: -------------------------------------------------------------------------------- 1 | from rest_framework.routers import DefaultRouter 2 | 3 | from . import views 4 | 5 | router = DefaultRouter() 6 | router.register(r"annotations", views.AnnotationsViewSet, basename="annotations") 7 | 8 | urlpatterns = router.urls 9 | -------------------------------------------------------------------------------- /apps/api/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techlib/celus/5ad1bd73a210cb5456eeea442913fa4f7649b7b5/apps/api/__init__.py -------------------------------------------------------------------------------- /apps/api/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | from rest_framework_api_key.admin import APIKeyModelAdmin 3 | 4 | from api.models import OrganizationAPIKey 5 | 6 | 7 | @admin.register(OrganizationAPIKey) 8 | class OrganizationAPIKeyAdmin(APIKeyModelAdmin): 9 | list_display = [*APIKeyModelAdmin.list_display, "organization"] 10 | search_fields = [*APIKeyModelAdmin.search_fields, "organization__name"] 11 | -------------------------------------------------------------------------------- /apps/api/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class ApiConfig(AppConfig): 5 | name = "api" 6 | -------------------------------------------------------------------------------- /apps/api/auth.py: -------------------------------------------------------------------------------- 1 | from typing import Optional 2 | 3 | from organizations.models import Organization 4 | from rest_framework_api_key.permissions import KeyParser 5 | 6 | from api.models import OrganizationAPIKey 7 | 8 | 9 | def extract_org_from_request_api_key(request) -> Optional[Organization]: 10 | key_parser = KeyParser() 11 | key = key_parser.get_from_authorization(request) 12 | if key: 13 | api_key = OrganizationAPIKey.objects.get_from_key(key) 14 | request._api_key = api_key 15 | return api_key.organization 16 | return None 17 | -------------------------------------------------------------------------------- /apps/api/fake_data.py: -------------------------------------------------------------------------------- 1 | import factory 2 | 3 | from api.models import OrganizationAPIKey 4 | 5 | 6 | class OrganizationAPIKeyFactory(factory.django.DjangoModelFactory): 7 | name = factory.Faker("name") 8 | prefix = factory.Faker("lexify", text="????????") 9 | id = factory.Faker("lexify") 10 | 11 | class Meta: 12 | model = OrganizationAPIKey 13 | -------------------------------------------------------------------------------- /apps/api/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techlib/celus/5ad1bd73a210cb5456eeea442913fa4f7649b7b5/apps/api/migrations/__init__.py -------------------------------------------------------------------------------- /apps/api/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | from rest_framework_api_key.models import AbstractAPIKey 3 | 4 | 5 | class OrganizationAPIKey(AbstractAPIKey): 6 | organization = models.ForeignKey( 7 | "organizations.Organization", on_delete=models.CASCADE, related_name="api_keys" 8 | ) 9 | -------------------------------------------------------------------------------- /apps/api/permissions.py: -------------------------------------------------------------------------------- 1 | from rest_framework_api_key.permissions import BaseHasAPIKey 2 | 3 | from api.models import OrganizationAPIKey 4 | 5 | 6 | class HasOrganizationAPIKey(BaseHasAPIKey): 7 | model = OrganizationAPIKey 8 | -------------------------------------------------------------------------------- /apps/api/tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techlib/celus/5ad1bd73a210cb5456eeea442913fa4f7649b7b5/apps/api/tests/__init__.py -------------------------------------------------------------------------------- /apps/api/tests/conftest.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | from logs.tests.conftest import flexible_slicer_test_data, report_type_nd # noqa 3 | from organizations.tests.conftest import organization_random # noqa - used by local tests 4 | 5 | from test_scenarios.basic import * # noqa 6 | 7 | 8 | @pytest.fixture 9 | def root_platform(platforms): 10 | return platforms["root"] 11 | 12 | 13 | @pytest.fixture 14 | def tr_report(report_types): 15 | return report_types["tr"] 16 | -------------------------------------------------------------------------------- /apps/api/throttling.py: -------------------------------------------------------------------------------- 1 | from core.logic.util import text_hash 2 | from rest_framework.throttling import SimpleRateThrottle 3 | 4 | 5 | class APIKeyBasedThrottle(SimpleRateThrottle): 6 | scope = "remote_api" 7 | 8 | def get_cache_key(self, request, view): 9 | """ 10 | Return the cache key for the current request. 11 | We use the value of the `Authorization` header as the key. This way if we give out 12 | multiple API keys to different users for the same organization, they will not interfere. 13 | """ 14 | return text_hash(request.headers.get("Authorization", "")) 15 | -------------------------------------------------------------------------------- /apps/charts/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techlib/celus/5ad1bd73a210cb5456eeea442913fa4f7649b7b5/apps/charts/__init__.py -------------------------------------------------------------------------------- /apps/charts/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class ChartsConfig(AppConfig): 5 | name = "charts" 6 | -------------------------------------------------------------------------------- /apps/charts/logic/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techlib/celus/5ad1bd73a210cb5456eeea442913fa4f7649b7b5/apps/charts/logic/__init__.py -------------------------------------------------------------------------------- /apps/charts/migrations/0006_chartdefinition_scope.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.5 on 2019-12-02 16:02 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | dependencies = [("charts", "0005_chartdefinition_ignore_platform")] 8 | 9 | operations = [ 10 | migrations.AddField( 11 | model_name="chartdefinition", 12 | name="scope", 13 | field=models.CharField( 14 | choices=[("", "any"), ("platform", "platform"), ("title", "title")], 15 | default="", 16 | max_length=10, 17 | ), 18 | ) 19 | ] 20 | -------------------------------------------------------------------------------- /apps/charts/migrations/0007_chartdefinition_scope_blank.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.9 on 2020-01-17 13:03 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | dependencies = [("charts", "0006_chartdefinition_scope")] 8 | 9 | operations = [ 10 | migrations.AlterField( 11 | model_name="chartdefinition", 12 | name="scope", 13 | field=models.CharField( 14 | blank=True, 15 | choices=[("", "any"), ("platform", "platform"), ("title", "title")], 16 | default="", 17 | max_length=10, 18 | ), 19 | ) 20 | ] 21 | -------------------------------------------------------------------------------- /apps/charts/migrations/0009_jsonfield.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.1.3 on 2020-11-20 13:38 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | dependencies = [("charts", "0008_datasource_set_null")] 8 | 9 | operations = [ 10 | migrations.AlterField( 11 | model_name="dimensionfilter", 12 | name="allowed_values", 13 | field=models.JSONField(blank=True, default=list), 14 | ), 15 | migrations.AlterField( 16 | model_name="reportdataview", 17 | name="metric_allowed_values", 18 | field=models.JSONField(blank=True, default=list), 19 | ), 20 | ] 21 | -------------------------------------------------------------------------------- /apps/charts/migrations/0011_remove_reportdataview_primary_dimension.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.2.17 on 2023-02-14 12:27 2 | 3 | from django.db import migrations 4 | 5 | 6 | class Migration(migrations.Migration): 7 | dependencies = [("charts", "0010_chartdefinition_is_generic")] 8 | 9 | operations = [migrations.RemoveField(model_name="reportdataview", name="primary_dimension")] 10 | -------------------------------------------------------------------------------- /apps/charts/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techlib/celus/5ad1bd73a210cb5456eeea442913fa4f7649b7b5/apps/charts/migrations/__init__.py -------------------------------------------------------------------------------- /apps/charts/tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techlib/celus/5ad1bd73a210cb5456eeea442913fa4f7649b7b5/apps/charts/tests/__init__.py -------------------------------------------------------------------------------- /apps/charts/translation.py: -------------------------------------------------------------------------------- 1 | from modeltranslation.translator import TranslationOptions, translator 2 | 3 | from charts.models import ChartDefinition 4 | 5 | from .models import ReportDataView 6 | 7 | 8 | class ReportDataViewTranslationOptions(TranslationOptions): 9 | fields = ("name", "desc") 10 | 11 | 12 | class ChartDefinitionTranslationOptions(TranslationOptions): 13 | fields = ("name", "desc") 14 | 15 | 16 | translator.register(ReportDataView, ReportDataViewTranslationOptions) 17 | translator.register(ChartDefinition, ChartDefinitionTranslationOptions) 18 | -------------------------------------------------------------------------------- /apps/core/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techlib/celus/5ad1bd73a210cb5456eeea442913fa4f7649b7b5/apps/core/__init__.py -------------------------------------------------------------------------------- /apps/core/admin_site.py: -------------------------------------------------------------------------------- 1 | from django.conf import settings 2 | from django.contrib import admin 3 | from django_otp import user_has_device 4 | 5 | 6 | class CelusAdminSite(admin.AdminSite): 7 | def has_permission(self, request): 8 | # Check for OTP in admin only when otp is enabled 9 | # and debug is turned off 10 | if ( 11 | settings.OTP_ENABLED 12 | and not settings.DEBUG 13 | and user_has_device(request.user) 14 | and not request.real_user.is_verified() 15 | and not request.real_user.skip_2fa 16 | ): 17 | return False 18 | 19 | return super().has_permission(request) 20 | -------------------------------------------------------------------------------- /apps/core/authentication.py: -------------------------------------------------------------------------------- 1 | from rest_framework.authentication import SessionAuthentication 2 | 3 | 4 | class SessionAuthentication401(SessionAuthentication): 5 | def authenticate_header(self, request): 6 | return "Session" 7 | -------------------------------------------------------------------------------- /apps/core/db.py: -------------------------------------------------------------------------------- 1 | from django.db.models import CharField, Lookup, TextField 2 | 3 | 4 | @TextField.register_lookup 5 | @CharField.register_lookup 6 | class ILike(Lookup): 7 | """ 8 | Custom lookup that uses ILIKE on Postgres which is much faster than the default approach 9 | when using a TRGM index 10 | """ 11 | 12 | lookup_name = "ilike" 13 | 14 | def as_sql(self, compiler, connection): 15 | lhs, lhs_params = self.process_lhs(compiler, connection) 16 | rhs, rhs_params = self.process_rhs(compiler, connection) 17 | params = lhs_params + rhs_params 18 | return f"{lhs} ILIKE CONCAT('%%', {rhs}, '%%')", params 19 | -------------------------------------------------------------------------------- /apps/core/exceptions.py: -------------------------------------------------------------------------------- 1 | from rest_framework.exceptions import APIException 2 | 3 | 4 | class BadRequestException(APIException): 5 | """ 6 | Exception to be used when we need to trigger 400 error response from a view. 7 | """ 8 | 9 | status_code = 400 10 | default_code = "bad request" 11 | default_detail = "Incorrect input data for the request" 12 | 13 | 14 | class ModelUsageError(Exception): 15 | """ 16 | Used when a model is used in a way that is not allowed or supported. 17 | """ 18 | 19 | 20 | class FileConsistencyError(Exception): 21 | """ 22 | Used when a file checksum does not match the stored value. 23 | """ 24 | -------------------------------------------------------------------------------- /apps/core/fields.py: -------------------------------------------------------------------------------- 1 | from rest_framework.fields import ListField 2 | 3 | 4 | class CompactListField(ListField): 5 | def get_value(self, dictionary): 6 | if val := dictionary.get(self.field_name): 7 | return val.split(",") 8 | return [] 9 | -------------------------------------------------------------------------------- /apps/core/filters.py: -------------------------------------------------------------------------------- 1 | from rest_framework import filters 2 | 3 | 4 | class PkMultiValueFilterBackend(filters.BaseFilterBackend): 5 | """ 6 | Filter that allows selection by a list of primary keys 7 | """ 8 | 9 | filter_field = "pk" 10 | query_param = "pks" 11 | 12 | def filter_queryset(self, request, queryset, view): 13 | pks = request.query_params.get(self.query_param) 14 | if pks: 15 | keys = pks.split(",") 16 | queryset = queryset.filter(**{f"{self.filter_field}__in": keys}) 17 | return queryset 18 | -------------------------------------------------------------------------------- /apps/core/logic/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techlib/celus/5ad1bd73a210cb5456eeea442913fa4f7649b7b5/apps/core/logic/__init__.py -------------------------------------------------------------------------------- /apps/core/logic/debug.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | from time import time 4 | 5 | import psutil 6 | 7 | 8 | def memory_usage_psutil(): 9 | process = psutil.Process(os.getpid()) 10 | mem = process.memory_full_info().rss / float(2**20) 11 | return mem 12 | 13 | 14 | def log_memory(name=""): 15 | usage = memory_usage_psutil() 16 | prefix = f"{name}: " if name else "" 17 | print(f"{prefix}Memory usage: {usage:.2f} MB; time: {time():.3f}", file=sys.stderr) 18 | return usage 19 | -------------------------------------------------------------------------------- /apps/core/logic/serialization.py: -------------------------------------------------------------------------------- 1 | import json 2 | from base64 import b64decode, b64encode 3 | from typing import Union 4 | 5 | 6 | def b64json(data): 7 | return b64encode(json.dumps(data).encode("utf-8")).decode("ascii") 8 | 9 | 10 | def parse_b64json(data: str) -> Union[dict, list]: 11 | return json.loads(b64decode(data)) 12 | -------------------------------------------------------------------------------- /apps/core/logic/util.py: -------------------------------------------------------------------------------- 1 | from hashlib import blake2b 2 | 3 | from django.conf import settings 4 | 5 | 6 | def text_hash(text: str): 7 | return blake2b(text.encode("utf-8"), digest_size=16).hexdigest() 8 | 9 | 10 | def this_celus_domain(): 11 | return settings.ALLOWED_HOSTS[0] 12 | -------------------------------------------------------------------------------- /apps/core/management/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techlib/celus/5ad1bd73a210cb5456eeea442913fa4f7649b7b5/apps/core/management/__init__.py -------------------------------------------------------------------------------- /apps/core/management/commands/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techlib/celus/5ad1bd73a210cb5456eeea442913fa4f7649b7b5/apps/core/management/commands/__init__.py -------------------------------------------------------------------------------- /apps/core/management/commands/sync_maximus.py: -------------------------------------------------------------------------------- 1 | from django.core.management.base import BaseCommand 2 | 3 | from core.logic.maximus_sync import sync 4 | 5 | 6 | class Command(BaseCommand): 7 | help = "Sync to celus-maximus" 8 | 9 | def handle(self, *args, **options): 10 | sync() 11 | -------------------------------------------------------------------------------- /apps/core/migrations/0004_identity_verbosename.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.1 on 2019-07-01 16:55 2 | 3 | from django.db import migrations 4 | 5 | 6 | class Migration(migrations.Migration): 7 | dependencies = [("core", "0003_datasource")] 8 | 9 | operations = [ 10 | migrations.AlterModelOptions(name="identity", options={"verbose_name_plural": "identities"}) 11 | ] 12 | -------------------------------------------------------------------------------- /apps/core/migrations/0005_user_language.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.4 on 2019-08-05 06:37 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | dependencies = [("core", "0004_identity_verbosename")] 8 | 9 | operations = [ 10 | migrations.AddField( 11 | model_name="user", 12 | name="language", 13 | field=models.CharField( 14 | choices=[("en", "English"), ("cs", "Czech")], 15 | default="cs", 16 | help_text="User's preferred language", 17 | max_length=2, 18 | ), 19 | ) 20 | ] 21 | -------------------------------------------------------------------------------- /apps/core/migrations/0006_blank_ext_id.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.4 on 2019-08-05 16:11 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | dependencies = [("core", "0005_user_language")] 8 | 9 | operations = [ 10 | migrations.AlterField( 11 | model_name="user", 12 | name="ext_id", 13 | field=models.PositiveIntegerField( 14 | blank=True, help_text="ID used in original source of this user data", null=True 15 | ), 16 | ) 17 | ] 18 | -------------------------------------------------------------------------------- /apps/core/migrations/0008_user_extra_data.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.14 on 2020-07-28 12:52 2 | 3 | import django.contrib.postgres.fields.jsonb 4 | from django.db import migrations 5 | 6 | 7 | class Migration(migrations.Migration): 8 | dependencies = [("core", "0007_timestamps")] 9 | 10 | operations = [ 11 | migrations.AddField( 12 | model_name="user", 13 | name="extra_data", 14 | field=django.contrib.postgres.fields.jsonb.JSONField( 15 | default=dict, help_text="User state data that do not deserve a dedicated field" 16 | ), 17 | ) 18 | ] 19 | -------------------------------------------------------------------------------- /apps/core/migrations/0009_user_extra_data_default.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.14 on 2020-08-12 17:34 2 | 3 | import django.contrib.postgres.fields.jsonb 4 | from django.db import migrations 5 | 6 | 7 | class Migration(migrations.Migration): 8 | dependencies = [("core", "0008_user_extra_data")] 9 | 10 | operations = [ 11 | migrations.AlterField( 12 | model_name="user", 13 | name="extra_data", 14 | field=django.contrib.postgres.fields.jsonb.JSONField( 15 | blank=True, 16 | default=dict, 17 | help_text="User state data that do not deserve a dedicated field", 18 | ), 19 | ) 20 | ] 21 | -------------------------------------------------------------------------------- /apps/core/migrations/0010_default_lang_change.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.15 on 2020-09-08 12:07 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | dependencies = [("core", "0009_user_extra_data_default")] 8 | 9 | operations = [ 10 | migrations.AlterField( 11 | model_name="user", 12 | name="language", 13 | field=models.CharField( 14 | choices=[("en", "English"), ("cs", "Czech")], 15 | default="en", 16 | help_text="User's preferred language", 17 | max_length=2, 18 | ), 19 | ) 20 | ] 21 | -------------------------------------------------------------------------------- /apps/core/migrations/0013_jsonfield.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.1.3 on 2020-11-20 13:38 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | dependencies = [("core", "0012_datasource_set_null")] 8 | 9 | operations = [ 10 | migrations.AlterField( 11 | model_name="user", 12 | name="extra_data", 13 | field=models.JSONField( 14 | blank=True, 15 | default=dict, 16 | help_text="User state data that do not deserve a dedicated field", 17 | ), 18 | ) 19 | ] 20 | -------------------------------------------------------------------------------- /apps/core/migrations/0014_auto_20210329_1002.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.1.6 on 2021-03-29 08:02 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | dependencies = [("core", "0013_jsonfield")] 8 | 9 | operations = [ 10 | migrations.AlterField( 11 | model_name="user", 12 | name="first_name", 13 | field=models.CharField(blank=True, max_length=150, verbose_name="first name"), 14 | ) 15 | ] 16 | -------------------------------------------------------------------------------- /apps/core/migrations/0016_taskprogress.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.2.13 on 2022-06-23 11:03 2 | 3 | from django.db import migrations 4 | 5 | 6 | class Migration(migrations.Migration): 7 | dependencies = [ 8 | ("django_celery_results", "0010_remove_duplicate_indices"), 9 | ("core", "0015_unique_name_within_organization_data_source"), 10 | ] 11 | 12 | operations = [ 13 | migrations.CreateModel( 14 | name="TaskProgress", 15 | fields=[], 16 | options={"proxy": True, "indexes": [], "constraints": []}, 17 | bases=("django_celery_results.taskresult",), 18 | ) 19 | ] 20 | -------------------------------------------------------------------------------- /apps/core/migrations/0018_pgcrypto.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.2.19 on 2023-06-06 05:42 2 | from django.contrib.postgres.operations import CryptoExtension 3 | from django.db import migrations 4 | 5 | 6 | class Migration(migrations.Migration): 7 | dependencies = [("core", "0017_user_extra_data_dismissed_and_seen_last_release")] 8 | 9 | operations = [CryptoExtension()] 10 | -------------------------------------------------------------------------------- /apps/core/migrations/0019_alter_user_managers.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.2.19 on 2023-06-27 15:53 2 | 3 | from django.db import migrations 4 | 5 | import core.models 6 | 7 | 8 | class Migration(migrations.Migration): 9 | dependencies = [("core", "0018_pgcrypto")] 10 | 11 | operations = [ 12 | migrations.AlterModelManagers( 13 | name="user", managers=[("objects", core.models.CelusUserManager())] 14 | ) 15 | ] 16 | -------------------------------------------------------------------------------- /apps/core/migrations/0021_user_skip_2fa.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 4.2.11 on 2024-04-30 09:22 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | dependencies = [("core", "0020_otp_devices")] 8 | 9 | operations = [ 10 | migrations.AddField( 11 | model_name="user", 12 | name="skip_2fa", 13 | field=models.BooleanField( 14 | default=False, help_text="If set to True, 2FA auth will be bypassed" 15 | ), 16 | ) 17 | ] 18 | -------------------------------------------------------------------------------- /apps/core/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techlib/celus/5ad1bd73a210cb5456eeea442913fa4f7649b7b5/apps/core/migrations/__init__.py -------------------------------------------------------------------------------- /apps/core/request_logging/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techlib/celus/5ad1bd73a210cb5456eeea442913fa4f7649b7b5/apps/core/request_logging/__init__.py -------------------------------------------------------------------------------- /apps/core/static/__do_not_remove__.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techlib/celus/5ad1bd73a210cb5456eeea442913fa4f7649b7b5/apps/core/static/__do_not_remove__.txt -------------------------------------------------------------------------------- /apps/core/task_support.py: -------------------------------------------------------------------------------- 1 | import logging 2 | from contextlib import contextmanager 3 | 4 | from django.core.cache import cache 5 | 6 | logger = logging.getLogger(__name__) 7 | 8 | 9 | @contextmanager 10 | def cache_based_lock(lock_name, timeout=3600, blocking_timeout=None): 11 | with cache.lock(lock_name, timeout=timeout, blocking_timeout=blocking_timeout): 12 | yield None 13 | -------------------------------------------------------------------------------- /apps/core/templates/account/email/email_confirmation_message.txt: -------------------------------------------------------------------------------- 1 | {% load account %}{% user_display user as user_display %}{% load i18n %}{% autoescape off %}{% blocktrans with site_name=current_site.name site_domain=current_site.domain %}Hello, 2 | 3 | you are receiving this email in order to verify your email address for use with Celus. 4 | 5 | To confirm your email, please go to {{ activate_url }} 6 | 7 | If you did not register your email with us, you can safely ignore this email or reply to it to let us know about the situation. 8 | 9 | Have a great day 10 | 11 | The Celus team 12 | {% endblocktrans %} 13 | {% endautoescape %} 14 | -------------------------------------------------------------------------------- /apps/core/templates/account/email/email_confirmation_signup_message.txt: -------------------------------------------------------------------------------- 1 | {% include 'account/email/email_confirmation_message.txt' %} 2 | -------------------------------------------------------------------------------- /apps/core/templates/account/email/email_confirmation_signup_subject.txt: -------------------------------------------------------------------------------- 1 | {% include 'account/email/email_confirmation_subject.txt' %} 2 | -------------------------------------------------------------------------------- /apps/core/templates/account/email/email_confirmation_subject.txt: -------------------------------------------------------------------------------- 1 | {% load i18n %} 2 | {% blocktrans %} 3 | Please verify your email address for use with Celus 4 | {% endblocktrans %} 5 | -------------------------------------------------------------------------------- /apps/core/templates/registration/invitation_email.html: -------------------------------------------------------------------------------- 1 | {% load i18n %}{% autoescape off %} 2 | {% blocktrans %}You're invited to register into the usage statistics application Celus at {{ site_name }}.{% endblocktrans %} 3 | 4 | {% trans "To accept the invitation, please go to the following page and choose your password:" %} 5 | {% block reset_link %} 6 | {{ protocol }}://{{ domain }}/accept-invitation/?uid={{ uid }}&token={{ token }} 7 | {% endblock %} 8 | 9 | {% trans "Looking forward to meeting you in Celus!" %} 10 | 11 | {% blocktrans %}The Celus team{% endblocktrans %} 12 | 13 | {% endautoescape %} 14 | -------------------------------------------------------------------------------- /apps/core/templates/registration/invitation_subject.txt: -------------------------------------------------------------------------------- 1 | Celus invitation 2 | -------------------------------------------------------------------------------- /apps/core/templates/registration/password_reset_message.html: -------------------------------------------------------------------------------- 1 | {% load i18n %}{% autoescape off %} 2 | {% blocktrans %}You're receiving this email because you requested a password reset for your Celus user account at {{ site_name }}.{% endblocktrans %} 3 | 4 | {% trans "Please go to the following page and choose a new password:" %} 5 | {% block reset_link %} 6 | {{ reset_url }} 7 | {% endblock %} 8 | {% trans "Your username, in case you've forgotten:" %} {{ user.email }} 9 | 10 | {% trans "Looking forward to seeing you in Celus again!" %} 11 | 12 | {% blocktrans %}The Celus team{% endblocktrans %} 13 | 14 | {% endautoescape %} 15 | -------------------------------------------------------------------------------- /apps/core/templates/registration/password_reset_subject.txt: -------------------------------------------------------------------------------- 1 | Celus password reset 2 | -------------------------------------------------------------------------------- /apps/core/tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techlib/celus/5ad1bd73a210cb5456eeea442913fa4f7649b7b5/apps/core/tests/__init__.py -------------------------------------------------------------------------------- /apps/core/tests/test_admin.py: -------------------------------------------------------------------------------- 1 | from django.urls import reverse 2 | 3 | 4 | def test_reverse_admin(): 5 | # '/custom-admin/' is configured in CELUS_ADMIN_SITE_PATH 6 | # which is set in pytest.ini 7 | assert "/custom-admin/" == reverse("admin:index") 8 | -------------------------------------------------------------------------------- /apps/cost/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techlib/celus/5ad1bd73a210cb5456eeea442913fa4f7649b7b5/apps/cost/__init__.py -------------------------------------------------------------------------------- /apps/cost/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class CostConfig(AppConfig): 5 | name = "cost" 6 | -------------------------------------------------------------------------------- /apps/cost/migrations/0002_payment_unique_together.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.5 on 2019-11-13 08:39 2 | 3 | from django.db import migrations 4 | 5 | 6 | class Migration(migrations.Migration): 7 | dependencies = [ 8 | ("organizations", "0013_auto_20191113_0939"), 9 | ("publications", "0014_remove_old_interest_reports_attr"), 10 | ("cost", "0001_initial"), 11 | ] 12 | 13 | operations = [ 14 | migrations.AlterUniqueTogether( 15 | name="payment", unique_together={("organization", "platform", "year")} 16 | ) 17 | ] 18 | -------------------------------------------------------------------------------- /apps/cost/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techlib/celus/5ad1bd73a210cb5456eeea442913fa4f7649b7b5/apps/cost/migrations/__init__.py -------------------------------------------------------------------------------- /apps/cost/serializers.py: -------------------------------------------------------------------------------- 1 | from rest_framework import serializers 2 | 3 | from .models import Payment 4 | 5 | 6 | class PaymentSerializer(serializers.ModelSerializer): 7 | class Meta: 8 | model = Payment 9 | fields = ["pk", "organization", "platform", "year", "price"] 10 | -------------------------------------------------------------------------------- /apps/cost/urls.py: -------------------------------------------------------------------------------- 1 | from organizations.urls import router as organization_router 2 | from rest_framework_nested.routers import NestedSimpleRouter 3 | 4 | from . import views 5 | 6 | org_sub_router = NestedSimpleRouter(organization_router, r"organization", lookup="organization") 7 | org_sub_router.register(r"payments", views.OrganizationPaymentViewSet, basename="payments") 8 | 9 | urlpatterns = [] + org_sub_router.urls 10 | -------------------------------------------------------------------------------- /apps/deployment/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techlib/celus/5ad1bd73a210cb5456eeea442913fa4f7649b7b5/apps/deployment/__init__.py -------------------------------------------------------------------------------- /apps/deployment/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | 3 | from .models import FooterImage, SiteLogo 4 | 5 | 6 | @admin.register(FooterImage) 7 | class FooterImageAdmin(admin.ModelAdmin): 8 | list_display = ("alt_text", "site", "img", "position", "last_modified") 9 | list_display_links = ("alt_text",) 10 | 11 | 12 | @admin.register(SiteLogo) 13 | class SiteLogoAdmin(admin.ModelAdmin): 14 | list_display = ("alt_text", "site", "img", "last_modified") 15 | list_display_links = ("alt_text",) 16 | -------------------------------------------------------------------------------- /apps/deployment/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class DeploymentConfig(AppConfig): 5 | name = "deployment" 6 | -------------------------------------------------------------------------------- /apps/deployment/migrations/0002_image_as_filefield.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.14 on 2020-07-21 14:47 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | dependencies = [("deployment", "0001_initial")] 8 | 9 | operations = [ 10 | migrations.AlterField( 11 | model_name="footerimage", name="img", field=models.FileField(upload_to="deployment") 12 | ), 13 | migrations.AlterField( 14 | model_name="sitelogo", name="img", field=models.FileField(upload_to="deployment") 15 | ), 16 | ] 17 | -------------------------------------------------------------------------------- /apps/deployment/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techlib/celus/5ad1bd73a210cb5456eeea442913fa4f7649b7b5/apps/deployment/migrations/__init__.py -------------------------------------------------------------------------------- /apps/deployment/tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techlib/celus/5ad1bd73a210cb5456eeea442913fa4f7649b7b5/apps/deployment/tests/__init__.py -------------------------------------------------------------------------------- /apps/deployment/tests/test_api.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | from core.tests.conftest import site # noqa - fixture 3 | from django.urls import reverse 4 | 5 | 6 | @pytest.mark.django_db 7 | class TestDeploymentAPI: 8 | def test_api_is_open(self, client, site): 9 | resp = client.get(reverse("deployment-overview")) 10 | assert resp.status_code == 200 11 | -------------------------------------------------------------------------------- /apps/deployment/urls.py: -------------------------------------------------------------------------------- 1 | from django.urls import path 2 | from rest_framework.routers import DefaultRouter 3 | 4 | from . import views 5 | 6 | router = DefaultRouter() 7 | router.register(r"footer-image", views.FooterImageViewSet, basename="footer-image") 8 | router.register(r"site-logo", views.SiteLogoViewSet, basename="site-logo") 9 | router.register(r"site", views.SiteViewSet, basename="site") 10 | 11 | urlpatterns = [path("overview/", views.SiteOverview.as_view(), name="deployment-overview")] 12 | 13 | urlpatterns += router.urls 14 | -------------------------------------------------------------------------------- /apps/erms/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techlib/celus/5ad1bd73a210cb5456eeea442913fa4f7649b7b5/apps/erms/__init__.py -------------------------------------------------------------------------------- /apps/erms/tests/conftest.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techlib/celus/5ad1bd73a210cb5456eeea442913fa4f7649b7b5/apps/erms/tests/conftest.py -------------------------------------------------------------------------------- /apps/events/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techlib/celus/5ad1bd73a210cb5456eeea442913fa4f7649b7b5/apps/events/__init__.py -------------------------------------------------------------------------------- /apps/events/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class EventsConfig(AppConfig): 5 | default_auto_field = "django.db.models.BigAutoField" 6 | name = "events" 7 | 8 | def ready(self): 9 | super().ready() 10 | from . import signals # noqa - needed to register the signals 11 | -------------------------------------------------------------------------------- /apps/events/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techlib/celus/5ad1bd73a210cb5456eeea442913fa4f7649b7b5/apps/events/migrations/__init__.py -------------------------------------------------------------------------------- /apps/events/tasks.py: -------------------------------------------------------------------------------- 1 | import logging 2 | 3 | import celery 4 | from core.context_managers import logged_task 5 | from core.logic.error_reporting import email_if_fails 6 | 7 | from events.models import UserEvent 8 | 9 | logger = logging.getLogger(__name__) 10 | 11 | 12 | @celery.shared_task 13 | @logged_task 14 | @email_if_fails 15 | def send_unsent_event_emails_task(): 16 | """ 17 | Send emails for all unsent events. 18 | """ 19 | logger.debug("Sent %d emails", UserEvent.objects.send_emails()) 20 | -------------------------------------------------------------------------------- /apps/events/templates/events/welcome_event_description.md: -------------------------------------------------------------------------------- 1 | Welcome on board. 2 | 3 | For basic information how to get around in Celus, we recommend our knowledge-base https://support.celus.net/support/solutions. 4 | 5 | In case you have any questions, let us know at ask@celus.net. We are happy to help you. 6 | -------------------------------------------------------------------------------- /apps/events/templates/events/welcome_event_title.txt: -------------------------------------------------------------------------------- 1 | Welcome to Celus 2 | -------------------------------------------------------------------------------- /apps/events/tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techlib/celus/5ad1bd73a210cb5456eeea442913fa4f7649b7b5/apps/events/tests/__init__.py -------------------------------------------------------------------------------- /apps/events/tests/conftest.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techlib/celus/5ad1bd73a210cb5456eeea442913fa4f7649b7b5/apps/events/tests/conftest.py -------------------------------------------------------------------------------- /apps/events/urls.py: -------------------------------------------------------------------------------- 1 | from rest_framework.routers import DefaultRouter 2 | 3 | from . import views 4 | 5 | router = DefaultRouter() 6 | router.register(r"user-events", views.UserEventsViewSet, basename="user-events") 7 | router.register( 8 | "user-preferences", views.UserEventPreferencesViewSet, basename="user-event-preferences" 9 | ) 10 | 11 | urlpatterns = [*router.urls] 12 | -------------------------------------------------------------------------------- /apps/export/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techlib/celus/5ad1bd73a210cb5456eeea442913fa4f7649b7b5/apps/export/__init__.py -------------------------------------------------------------------------------- /apps/export/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class ExportConfig(AppConfig): 5 | name = "export" 6 | 7 | def ready(self): 8 | from . import signals # noqa 9 | -------------------------------------------------------------------------------- /apps/export/enums.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | 3 | 4 | class FileFormat(models.TextChoices): 5 | XLSX = "XLSX", "XLSX" 6 | XLSX_NO_CHARTS = "XLSX_NO_CHARTS", "XLSX without charts" 7 | ZIP_CSV = "ZIP_CSV", "CSV files inside ZIP archive" 8 | 9 | @classmethod 10 | def file_extension(cls, value): 11 | if value in (cls.XLSX, cls.XLSX_NO_CHARTS): 12 | return "xlsx" 13 | else: 14 | return "zip" 15 | -------------------------------------------------------------------------------- /apps/export/migrations/0003_created_autoaddnow.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.1.6 on 2021-04-01 14:25 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | dependencies = [("export", "0002_auto_20210329_1002")] 8 | 9 | operations = [ 10 | migrations.AlterField( 11 | model_name="flexibledataexport", 12 | name="created", 13 | field=models.DateTimeField(auto_now_add=True), 14 | ) 15 | ] 16 | -------------------------------------------------------------------------------- /apps/export/migrations/0004_export_status_error.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.1.8 on 2021-04-20 15:41 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | dependencies = [("export", "0003_created_autoaddnow")] 8 | 9 | operations = [ 10 | migrations.AlterField( 11 | model_name="flexibledataexport", 12 | name="status", 13 | field=models.PositiveSmallIntegerField( 14 | choices=[(0, "not started"), (1, "in progress"), (2, "finished"), (3, "error")], 15 | default=0, 16 | ), 17 | ) 18 | ] 19 | -------------------------------------------------------------------------------- /apps/export/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techlib/celus/5ad1bd73a210cb5456eeea442913fa4f7649b7b5/apps/export/migrations/__init__.py -------------------------------------------------------------------------------- /apps/export/signals.py: -------------------------------------------------------------------------------- 1 | from django.db.models.signals import post_delete 2 | from django.dispatch import receiver 3 | 4 | from .models import FlexibleDataExport 5 | 6 | 7 | @receiver(post_delete, sender=FlexibleDataExport) 8 | def delete_export_file_from_direcotry(sender, instance, **kwargs): 9 | instance.output_file.delete(save=False) 10 | -------------------------------------------------------------------------------- /apps/export/tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techlib/celus/5ad1bd73a210cb5456eeea442913fa4f7649b7b5/apps/export/tests/__init__.py -------------------------------------------------------------------------------- /apps/export/tests/conftest.py: -------------------------------------------------------------------------------- 1 | from logs.tests.conftest import flexible_slicer_test_data, report_type_nd # noqa - fixtures 2 | 3 | from test_scenarios.basic import users # noqa - fixtures 4 | -------------------------------------------------------------------------------- /apps/export/urls.py: -------------------------------------------------------------------------------- 1 | from rest_framework.routers import DefaultRouter 2 | 3 | from . import views 4 | 5 | router = DefaultRouter() 6 | router.register(r"flexible-export", views.FlexibleDataExportViewSet, basename="flexible-export") 7 | 8 | urlpatterns = router.urls 9 | -------------------------------------------------------------------------------- /apps/impersonate_api/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techlib/celus/5ad1bd73a210cb5456eeea442913fa4f7649b7b5/apps/impersonate_api/__init__.py -------------------------------------------------------------------------------- /apps/impersonate_api/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class ImpersonateApiConfig(AppConfig): 5 | default_auto_field = "django.db.models.BigAutoField" 6 | name = "impersonate_api" 7 | -------------------------------------------------------------------------------- /apps/impersonate_api/urls.py: -------------------------------------------------------------------------------- 1 | from rest_framework.routers import SimpleRouter 2 | 3 | from .views import ImpersonateViewSet 4 | 5 | router = SimpleRouter() 6 | router.register("impersonate", ImpersonateViewSet, basename="impersonate") 7 | 8 | urlpatterns = router.urls 9 | -------------------------------------------------------------------------------- /apps/knowledgebase/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techlib/celus/5ad1bd73a210cb5456eeea442913fa4f7649b7b5/apps/knowledgebase/__init__.py -------------------------------------------------------------------------------- /apps/knowledgebase/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class KnowledgebaseConfig(AppConfig): 5 | name = "knowledgebase" 6 | 7 | def ready(self): 8 | from . import signals # noqa 9 | -------------------------------------------------------------------------------- /apps/knowledgebase/management/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techlib/celus/5ad1bd73a210cb5456eeea442913fa4f7649b7b5/apps/knowledgebase/management/__init__.py -------------------------------------------------------------------------------- /apps/knowledgebase/management/commands/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techlib/celus/5ad1bd73a210cb5456eeea442913fa4f7649b7b5/apps/knowledgebase/management/commands/__init__.py -------------------------------------------------------------------------------- /apps/knowledgebase/migrations/0002_jsonfield.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.1.3 on 2020-11-20 13:38 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | dependencies = [("knowledgebase", "0001_initial")] 8 | 9 | operations = [ 10 | migrations.AlterField( 11 | model_name="importattempt", name="stats", field=models.JSONField(blank=True, null=True) 12 | ) 13 | ] 14 | -------------------------------------------------------------------------------- /apps/knowledgebase/migrations/0006_alter_routersyncattempt_last_error.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.2.19 on 2023-08-02 13:12 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | dependencies = [("knowledgebase", "0005_nibbler")] 8 | 9 | operations = [ 10 | migrations.AlterField( 11 | model_name="routersyncattempt", 12 | name="last_error", 13 | field=models.TextField(blank=True, default=""), 14 | ) 15 | ] 16 | -------------------------------------------------------------------------------- /apps/knowledgebase/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techlib/celus/5ad1bd73a210cb5456eeea442913fa4f7649b7b5/apps/knowledgebase/migrations/__init__.py -------------------------------------------------------------------------------- /apps/knowledgebase/templates/knowledgebase/importattempt_changelist.html: -------------------------------------------------------------------------------- 1 | {% extends 'admin/change_list.html' %} 2 | 3 | {% block object-tools %} 4 |
5 |
6 | {% csrf_token %} 7 | 12 | 13 | 14 | 15 |
16 |
17 |
18 | {{ block.super }} 19 | {% endblock %} 20 | -------------------------------------------------------------------------------- /apps/logs/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techlib/celus/5ad1bd73a210cb5456eeea442913fa4f7649b7b5/apps/logs/__init__.py -------------------------------------------------------------------------------- /apps/logs/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class LogsConfig(AppConfig): 5 | name = "logs" 6 | 7 | def ready(self): 8 | super().ready() 9 | from . import signals # noqa - needed to register the signals 10 | -------------------------------------------------------------------------------- /apps/logs/constants.py: -------------------------------------------------------------------------------- 1 | ACTION_INTEREST_CHANGE = "interest_definition_change" 2 | ACTION_INTEREST_SMART_SYNC = "interest_smart_sync" 3 | -------------------------------------------------------------------------------- /apps/logs/logic/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techlib/celus/5ad1bd73a210cb5456eeea442913fa4f7649b7b5/apps/logs/logic/__init__.py -------------------------------------------------------------------------------- /apps/logs/logic/remap.py: -------------------------------------------------------------------------------- 1 | from ..models import Dimension, DimensionText 2 | 3 | 4 | def remap_dicts(dimension: Dimension, records: [dict], key): 5 | mapping = { 6 | dt.pk: dt.text_local or dt.text for dt in DimensionText.objects.filter(dimension=dimension) 7 | } 8 | for record in records: 9 | record[key] = mapping.get(record.get(key)) 10 | -------------------------------------------------------------------------------- /apps/logs/logic/reporting/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techlib/celus/5ad1bd73a210cb5456eeea442913fa4f7649b7b5/apps/logs/logic/reporting/__init__.py -------------------------------------------------------------------------------- /apps/logs/management/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techlib/celus/5ad1bd73a210cb5456eeea442913fa4f7649b7b5/apps/logs/management/__init__.py -------------------------------------------------------------------------------- /apps/logs/management/commands/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techlib/celus/5ad1bd73a210cb5456eeea442913fa4f7649b7b5/apps/logs/management/commands/__init__.py -------------------------------------------------------------------------------- /apps/logs/migrations/0002_add_dimension_sorting.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.1 on 2019-06-24 14:44 2 | 3 | from django.db import migrations 4 | 5 | 6 | class Migration(migrations.Migration): 7 | dependencies = [("logs", "0001_initial")] 8 | 9 | operations = [ 10 | migrations.AlterModelOptions( 11 | name="dimension", options={"ordering": ("reporttypetodimension",)} 12 | ), 13 | migrations.AlterModelOptions( 14 | name="reporttypetodimension", options={"ordering": ("position",)} 15 | ), 16 | ] 17 | -------------------------------------------------------------------------------- /apps/logs/migrations/0005_metric_active.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.1 on 2019-08-06 14:27 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | dependencies = [("logs", "0004_source_field")] 8 | 9 | operations = [ 10 | migrations.AddField( 11 | model_name="metric", 12 | name="active", 13 | field=models.BooleanField( 14 | default=True, help_text="Only active metrics are reported to users" 15 | ), 16 | ) 17 | ] 18 | -------------------------------------------------------------------------------- /apps/logs/migrations/0009_accesslog_import_batch.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.4 on 2019-08-16 07:49 2 | 3 | import django.db.models.deletion 4 | from django.db import migrations, models 5 | 6 | 7 | class Migration(migrations.Migration): 8 | dependencies = [("logs", "0008_importbatch")] 9 | 10 | operations = [ 11 | migrations.AddField( 12 | model_name="accesslog", 13 | name="import_batch", 14 | field=models.ForeignKey( 15 | default=None, on_delete=django.db.models.deletion.CASCADE, to="logs.ImportBatch" 16 | ), 17 | preserve_default=False, 18 | ) 19 | ] 20 | -------------------------------------------------------------------------------- /apps/logs/migrations/0010_verbose_name_plural.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.4 on 2019-08-16 08:33 2 | 3 | from django.db import migrations 4 | 5 | 6 | class Migration(migrations.Migration): 7 | dependencies = [("logs", "0009_accesslog_import_batch")] 8 | 9 | operations = [ 10 | migrations.AlterModelOptions( 11 | name="importbatch", options={"verbose_name_plural": "Import batches"} 12 | ) 13 | ] 14 | -------------------------------------------------------------------------------- /apps/logs/migrations/0014_default_dimension_type.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.4 on 2019-08-24 15:50 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | dependencies = [("logs", "0013_nullable_title_mdu_validation")] 8 | 9 | operations = [ 10 | migrations.AlterField( 11 | model_name="dimension", 12 | name="type", 13 | field=models.PositiveSmallIntegerField( 14 | choices=[(1, "integer"), (2, "text")], default=2 15 | ), 16 | ) 17 | ] 18 | -------------------------------------------------------------------------------- /apps/logs/migrations/0015_change_data_file_validators.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.4 on 2019-08-29 07:37 2 | 3 | from django.db import migrations, models 4 | 5 | import logs.models 6 | 7 | 8 | class Migration(migrations.Migration): 9 | dependencies = [("logs", "0014_default_dimension_type")] 10 | 11 | operations = [ 12 | migrations.AlterField( 13 | model_name="manualdataupload", 14 | name="data_file", 15 | field=models.FileField( 16 | upload_to=logs.models.where_to_store, validators=[logs.models.validate_mime_type] 17 | ), 18 | ) 19 | ] 20 | -------------------------------------------------------------------------------- /apps/logs/migrations/0022_move_some_models_to_charts.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.5 on 2019-09-09 08:47 2 | 3 | from django.db import migrations 4 | 5 | 6 | class Migration(migrations.Migration): 7 | dependencies = [("logs", "0021_virtual_report_type_to_report_data_view")] 8 | 9 | operations = [ 10 | migrations.RemoveField(model_name="reportdataview", name="base_report_type"), 11 | migrations.RemoveField(model_name="reportdataview", name="primary_dimension"), 12 | migrations.RemoveField(model_name="reportdataview", name="source"), 13 | migrations.DeleteModel(name="DimensionFilter"), 14 | migrations.DeleteModel(name="ReportDataView"), 15 | ] 16 | -------------------------------------------------------------------------------- /apps/logs/migrations/0023_importbatch_interest_processed.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.4 on 2019-09-13 09:26 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | dependencies = [("logs", "0022_move_some_models_to_charts")] 8 | 9 | operations = [ 10 | migrations.AddField( 11 | model_name="importbatch", 12 | name="interest_processed", 13 | field=models.BooleanField( 14 | default=False, help_text="Was interest already calculated for this import batch" 15 | ), 16 | ) 17 | ] 18 | -------------------------------------------------------------------------------- /apps/logs/migrations/0024_no_reportinterestmetric_name.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.5 on 2019-09-20 14:09 2 | 3 | from django.db import migrations 4 | 5 | 6 | class Migration(migrations.Migration): 7 | dependencies = [("logs", "0023_importbatch_interest_processed")] 8 | 9 | operations = [ 10 | migrations.RemoveField(model_name="reportinterestmetric", name="name"), 11 | migrations.RemoveField(model_name="reportinterestmetric", name="name_cs"), 12 | migrations.RemoveField(model_name="reportinterestmetric", name="name_en"), 13 | ] 14 | -------------------------------------------------------------------------------- /apps/logs/migrations/0027_importbatch_interest_timestamp.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.5 on 2019-11-06 09:04 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | dependencies = [("logs", "0026_interest_group_ordering")] 8 | 9 | operations = [ 10 | migrations.AddField( 11 | model_name="importbatch", 12 | name="interest_timestamp", 13 | field=models.DateTimeField( 14 | blank=True, help_text="When was interest procesed for this batch", null=True 15 | ), 16 | ) 17 | ] 18 | -------------------------------------------------------------------------------- /apps/logs/migrations/0041_merge_20210327_1013.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.1.6 on 2021-03-27 09:13 2 | 3 | from django.db import migrations 4 | 5 | 6 | class Migration(migrations.Migration): 7 | dependencies = [ 8 | ("logs", "0040_reporttype_approx_record_count"), 9 | ("logs", "0038_reporttype_materialization_date"), 10 | ] 11 | 12 | operations = [] 13 | -------------------------------------------------------------------------------- /apps/logs/migrations/0050_import_batch_date_remove_import_batch_from_mdu.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.2.11 on 2022-01-24 13:27 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | dependencies = [("logs", "0049_mdu_many_importbatches")] 8 | 9 | operations = [ 10 | migrations.RemoveField(model_name="manualdataupload", name="import_batch"), 11 | migrations.AddField( 12 | model_name="importbatch", name="date", field=models.DateField(null=True) 13 | ), 14 | ] 15 | -------------------------------------------------------------------------------- /apps/logs/migrations/0052_remove_importbatch_system_created.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.2.10 on 2022-02-01 09:04 2 | 3 | from django.db import migrations 4 | 5 | 6 | class Migration(migrations.Migration): 7 | dependencies = [("logs", "0051_split_import_batches")] 8 | 9 | operations = [migrations.RemoveField(model_name="importbatch", name="system_created")] 10 | -------------------------------------------------------------------------------- /apps/logs/migrations/0055_accesslog_logs_access_platfor_cdb3c4_idx.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.2.12 on 2022-02-17 07:57 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | dependencies = [("logs", "0054_fill_importbatch_date")] 8 | 9 | operations = [ 10 | migrations.AddIndex( 11 | model_name="accesslog", 12 | index=models.Index( 13 | fields=["platform", "organization", "report_type"], 14 | name="logs_access_platfor_cdb3c4_idx", 15 | ), 16 | ) 17 | ] 18 | -------------------------------------------------------------------------------- /apps/logs/migrations/0056_importbatch_logs_import_date_brin.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.2.12 on 2022-02-23 09:12 2 | 3 | import django.contrib.postgres.indexes 4 | from django.db import migrations 5 | 6 | 7 | class Migration(migrations.Migration): 8 | dependencies = [("logs", "0055_accesslog_logs_access_platfor_cdb3c4_idx")] 9 | 10 | operations = [ 11 | migrations.AddIndex( 12 | model_name="importbatch", 13 | index=django.contrib.postgres.indexes.BrinIndex( 14 | fields=["date"], name="logs_import_date_269c60_brin" 15 | ), 16 | ) 17 | ] 18 | -------------------------------------------------------------------------------- /apps/logs/migrations/0058_manualdataupload_error.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.2.12 on 2022-02-25 22:23 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | dependencies = [("logs", "0057_rename_extra_manualdataupload_preflight")] 8 | 9 | operations = [ 10 | migrations.AddField( 11 | model_name="manualdataupload", 12 | name="error", 13 | field=models.CharField(blank=True, max_length=50, null=True), 14 | ) 15 | ] 16 | -------------------------------------------------------------------------------- /apps/logs/migrations/0061_remove_manualdataupload_is_processed.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.2.12 on 2022-03-16 13:24 2 | 3 | from django.db import migrations 4 | 5 | 6 | class Migration(migrations.Migration): 7 | dependencies = [("logs", "0060_controlled_metrics")] 8 | 9 | operations = [migrations.RemoveField(model_name="manualdataupload", name="is_processed")] 10 | -------------------------------------------------------------------------------- /apps/logs/migrations/0062_merge_20220325_1628.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.2.12 on 2022-03-25 15:28 2 | 3 | from django.db import migrations 4 | 5 | 6 | class Migration(migrations.Migration): 7 | dependencies = [ 8 | ("logs", "0057_verbose_names"), 9 | ("logs", "0061_remove_manualdataupload_is_processed"), 10 | ] 11 | 12 | operations = [] 13 | -------------------------------------------------------------------------------- /apps/logs/migrations/0063_alter_reportinterestmetric_interest_group.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.2.12 on 2022-03-30 06:18 2 | 3 | import django.db.models.deletion 4 | from django.db import migrations, models 5 | 6 | 7 | class Migration(migrations.Migration): 8 | dependencies = [("logs", "0062_merge_20220325_1628")] 9 | 10 | operations = [ 11 | migrations.AlterField( 12 | model_name="reportinterestmetric", 13 | name="interest_group", 14 | field=models.ForeignKey( 15 | on_delete=django.db.models.deletion.CASCADE, to="logs.interestgroup" 16 | ), 17 | ) 18 | ] 19 | -------------------------------------------------------------------------------- /apps/logs/migrations/0064_manualdataupload_error_details.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.2.12 on 2022-03-29 14:30 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | dependencies = [("logs", "0063_alter_reportinterestmetric_interest_group")] 8 | 9 | operations = [ 10 | migrations.AddField( 11 | model_name="manualdataupload", 12 | name="error_details", 13 | field=models.JSONField(blank=True, null=True), 14 | ) 15 | ] 16 | -------------------------------------------------------------------------------- /apps/logs/migrations/0065_remove_dimension_type.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.2.12 on 2022-04-14 05:58 2 | 3 | from django.db import migrations 4 | 5 | 6 | class Migration(migrations.Migration): 7 | dependencies = [("logs", "0064_manualdataupload_error_details")] 8 | 9 | operations = [migrations.RemoveField(model_name="dimension", name="type")] 10 | -------------------------------------------------------------------------------- /apps/logs/migrations/0069_merge_20220603_1325.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.2.13 on 2022-06-03 11:25 2 | 3 | from django.db import migrations 4 | 5 | 6 | class Migration(migrations.Migration): 7 | dependencies = [ 8 | ("logs", "0067_alter_importbatchsynclog_state"), 9 | ("logs", "0068_file_checksums"), 10 | ] 11 | 12 | operations = [] 13 | -------------------------------------------------------------------------------- /apps/logs/migrations/0071_mdu_import_batch_ordering.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.2.15 on 2022-10-21 07:57 2 | 3 | from django.db import migrations 4 | 5 | 6 | class Migration(migrations.Migration): 7 | dependencies = [("logs", "0070_reporttype_ext_id")] 8 | 9 | operations = [ 10 | migrations.AlterModelOptions( 11 | name="importbatch", 12 | options={"ordering": ("id",), "verbose_name_plural": "Import batches"}, 13 | ), 14 | migrations.AlterModelOptions( 15 | name="manualdatauploadimportbatch", options={"ordering": ("mdu_id", "import_batch_id")} 16 | ), 17 | ] 18 | -------------------------------------------------------------------------------- /apps/logs/migrations/0073_alter_reportinterestmetric_unique_together.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.2.15 on 2022-10-26 13:09 2 | 3 | from django.db import migrations 4 | 5 | 6 | class Migration(migrations.Migration): 7 | dependencies = [("logs", "0072_nibbler")] 8 | 9 | operations = [ 10 | migrations.AlterUniqueTogether( 11 | name="reportinterestmetric", 12 | unique_together={("interest_group", "metric", "report_type")}, 13 | ) 14 | ] 15 | -------------------------------------------------------------------------------- /apps/logs/migrations/0078_remove_organizationplatform_sushi_credentials.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.2.19 on 2023-07-08 09:48 2 | 3 | from django.db import migrations 4 | 5 | 6 | class Migration(migrations.Migration): 7 | dependencies = [("logs", "0077_remove_source_from_dimension")] 8 | 9 | operations = [ 10 | migrations.RemoveField(model_name="organizationplatform", name="sushi_credentials") 11 | ] 12 | -------------------------------------------------------------------------------- /apps/logs/migrations/0079_alter_importbatch_unique_together.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.2.20 on 2023-08-14 07:18 2 | 3 | from django.db import migrations 4 | 5 | 6 | class Migration(migrations.Migration): 7 | dependencies = [ 8 | ("publications", "0037_add_ir_m1_interest_to_all_platforms"), 9 | ("organizations", "0024_alter_userorganization_unique_together"), 10 | ("logs", "0078_remove_organizationplatform_sushi_credentials"), 11 | ] 12 | 13 | operations = [ 14 | migrations.AlterUniqueTogether( 15 | name="importbatch", 16 | unique_together={("report_type", "organization", "platform", "date")}, 17 | ) 18 | ] 19 | -------------------------------------------------------------------------------- /apps/logs/migrations/0082_importbatch_manual_empty.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 4.2.11 on 2024-03-20 11:55 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | dependencies = [("logs", "0081_mdu_extra_and_constraints")] 8 | 9 | operations = [ 10 | migrations.AddField( 11 | model_name="importbatch", 12 | name="manual_empty", 13 | field=models.BooleanField( 14 | default=False, help_text="If the batch was created manually by the user as empty" 15 | ), 16 | ) 17 | ] 18 | -------------------------------------------------------------------------------- /apps/logs/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techlib/celus/5ad1bd73a210cb5456eeea442913fa4f7649b7b5/apps/logs/migrations/__init__.py -------------------------------------------------------------------------------- /apps/logs/static/css/report_type.css: -------------------------------------------------------------------------------- 1 | td.field-record_count { 2 | text-align: right; 3 | } 4 | -------------------------------------------------------------------------------- /apps/logs/tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techlib/celus/5ad1bd73a210cb5456eeea442913fa4f7649b7b5/apps/logs/tests/__init__.py -------------------------------------------------------------------------------- /apps/logs/tests/data: -------------------------------------------------------------------------------- 1 | ../../../test-data/ -------------------------------------------------------------------------------- /apps/logs/tests/test_export_utils.py: -------------------------------------------------------------------------------- 1 | from io import StringIO 2 | 3 | from ..logic.export_utils import MappingCSVDictWriter 4 | 5 | 6 | class TestMappingDictWriter: 7 | def test_simple(self): 8 | out = StringIO() 9 | writer = MappingCSVDictWriter(out, fields=[("a", "A"), ("b", "B")]) 10 | writer.writerow({"a": 1, "b": 2}) 11 | assert out.getvalue().splitlines() == ["A,B", "1,2"] 12 | -------------------------------------------------------------------------------- /apps/necronomicon/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techlib/celus/5ad1bd73a210cb5456eeea442913fa4f7649b7b5/apps/necronomicon/__init__.py -------------------------------------------------------------------------------- /apps/necronomicon/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class NecronomiconConfig(AppConfig): 5 | default_auto_field = "django.db.models.BigAutoField" 6 | name = "necronomicon" 7 | -------------------------------------------------------------------------------- /apps/necronomicon/fake_data.py: -------------------------------------------------------------------------------- 1 | import factory 2 | 3 | from necronomicon.models import Batch, Candidate 4 | 5 | 6 | class BatchFactory(factory.django.DjangoModelFactory): 7 | class Meta: 8 | model = Batch 9 | 10 | 11 | class CandidateFactory(factory.django.DjangoModelFactory): 12 | batch = factory.SubFactory(Batch) 13 | 14 | class Meta: 15 | model = Candidate 16 | -------------------------------------------------------------------------------- /apps/necronomicon/migrations/0002_drop_fk_for_task_result.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.2.17 on 2023-02-15 15:23 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | dependencies = [("necronomicon", "0001_initial")] 8 | 9 | operations = [ 10 | migrations.RemoveField(model_name="batch", name="task_result"), 11 | migrations.AddField( 12 | model_name="batch", 13 | name="task_result_id", 14 | field=models.IntegerField(blank=True, null=True), 15 | ), 16 | ] 17 | -------------------------------------------------------------------------------- /apps/necronomicon/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techlib/celus/5ad1bd73a210cb5456eeea442913fa4f7649b7b5/apps/necronomicon/migrations/__init__.py -------------------------------------------------------------------------------- /apps/necronomicon/templates/necronomicon/admin/change_form.html: -------------------------------------------------------------------------------- 1 | {% extends 'admin/change_form.html' %} 2 | {% load i18n %} 3 | 4 | {% block submit_buttons_bottom %} 5 |
6 | {% if original.can_delete %} 7 | 10 | {% endif %} 11 | {% trans 'Close' %} 12 |
13 | {% endblock %} 14 | -------------------------------------------------------------------------------- /apps/nibbler/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techlib/celus/5ad1bd73a210cb5456eeea442913fa4f7649b7b5/apps/nibbler/__init__.py -------------------------------------------------------------------------------- /apps/nibbler/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class NibblerConfig(AppConfig): 5 | default_auto_field = "django.db.models.BigAutoField" 6 | name = "nibbler" 7 | -------------------------------------------------------------------------------- /apps/nibbler/fake_data.py: -------------------------------------------------------------------------------- 1 | import factory 2 | 3 | from nibbler.models import ParserDefinition 4 | 5 | 6 | class ParserDefinitionFactory(factory.django.DjangoModelFactory): 7 | class Meta: 8 | model = ParserDefinition 9 | django_get_or_create = ("id",) 10 | 11 | report_type_short_name = factory.LazyAttribute(lambda x: x.definition["data_format"]["name"]) 12 | short_name = factory.LazyAttribute(lambda x: x.definition["parser_name"]) 13 | version = factory.LazyAttribute(lambda x: x.definition["version"]) 14 | -------------------------------------------------------------------------------- /apps/nibbler/logic/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techlib/celus/5ad1bd73a210cb5456eeea442913fa4f7649b7b5/apps/nibbler/logic/__init__.py -------------------------------------------------------------------------------- /apps/nibbler/logic/dict_reader.py: -------------------------------------------------------------------------------- 1 | from celus_nibbler.reader import CsvReader, DictReader 2 | 3 | 4 | def get_dict_reader_from_csv(infile, sheet_num: int = 0) -> DictReader: 5 | sheets = CsvReader(infile) 6 | return sheets[sheet_num].dict_reader() 7 | -------------------------------------------------------------------------------- /apps/nibbler/logic/utils.py: -------------------------------------------------------------------------------- 1 | def all_nibbler_counter_parsers(json_format: bool = False) -> str: 2 | name = "Json" if json_format else "Tabular" 3 | return f"static\\.counter[^\\.]+\\.[^\\.]+.{name}" 4 | -------------------------------------------------------------------------------- /apps/nibbler/management/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techlib/celus/5ad1bd73a210cb5456eeea442913fa4f7649b7b5/apps/nibbler/management/__init__.py -------------------------------------------------------------------------------- /apps/nibbler/management/commands/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techlib/celus/5ad1bd73a210cb5456eeea442913fa4f7649b7b5/apps/nibbler/management/commands/__init__.py -------------------------------------------------------------------------------- /apps/nibbler/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techlib/celus/5ad1bd73a210cb5456eeea442913fa4f7649b7b5/apps/nibbler/migrations/__init__.py -------------------------------------------------------------------------------- /apps/organizations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techlib/celus/5ad1bd73a210cb5456eeea442913fa4f7649b7b5/apps/organizations/__init__.py -------------------------------------------------------------------------------- /apps/organizations/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class OrganizationsConfig(AppConfig): 5 | name = "organizations" 6 | -------------------------------------------------------------------------------- /apps/organizations/logic/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techlib/celus/5ad1bd73a210cb5456eeea442913fa4f7649b7b5/apps/organizations/logic/__init__.py -------------------------------------------------------------------------------- /apps/organizations/management/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techlib/celus/5ad1bd73a210cb5456eeea442913fa4f7649b7b5/apps/organizations/management/__init__.py -------------------------------------------------------------------------------- /apps/organizations/management/commands/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techlib/celus/5ad1bd73a210cb5456eeea442913fa4f7649b7b5/apps/organizations/management/commands/__init__.py -------------------------------------------------------------------------------- /apps/organizations/management/commands/erms_sync_organizations.py: -------------------------------------------------------------------------------- 1 | import logging 2 | 3 | from django.core.management.base import BaseCommand 4 | from django.db.transaction import atomic 5 | 6 | from ...logic.sync import erms_sync_organizations 7 | 8 | logger = logging.getLogger(__name__) 9 | 10 | 11 | class Command(BaseCommand): 12 | help = "Sync organizations between ERMS and the database" 13 | 14 | def add_arguments(self, parser): 15 | pass 16 | 17 | @atomic 18 | def handle(self, *args, **options): 19 | stats = erms_sync_organizations() 20 | self.stderr.write(self.style.WARNING(f"Import stats: {stats}")) 21 | -------------------------------------------------------------------------------- /apps/organizations/migrations/0003_non_unique_ico.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.1 on 2019-06-26 13:12 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | dependencies = [("organizations", "0002_nullable_internal_id")] 8 | 9 | operations = [ 10 | migrations.AlterField( 11 | model_name="organization", 12 | name="ico", 13 | field=models.PositiveIntegerField(help_text="Business registration number"), 14 | ), 15 | migrations.AlterUniqueTogether(name="organization", unique_together={("ico", "level")}), 16 | ] 17 | -------------------------------------------------------------------------------- /apps/organizations/migrations/0008_sushicredentials_enabled.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.1 on 2019-08-01 16:18 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | dependencies = [("organizations", "0007_sushicredentials")] 8 | 9 | operations = [ 10 | migrations.AddField( 11 | model_name="sushicredentials", name="enabled", field=models.BooleanField(default=True) 12 | ) 13 | ] 14 | -------------------------------------------------------------------------------- /apps/organizations/migrations/0010_delete_sushicredentials.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.1 on 2019-08-02 08:22 2 | 3 | from django.db import migrations 4 | 5 | 6 | class Migration(migrations.Migration): 7 | dependencies = [("organizations", "0009_sushi_credentials_tuning")] 8 | 9 | operations = [migrations.DeleteModel(name="SushiCredentials")] 10 | -------------------------------------------------------------------------------- /apps/organizations/migrations/0013_auto_20191113_0939.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.5 on 2019-11-13 08:39 2 | 3 | from django.db import migrations 4 | 5 | 6 | class Migration(migrations.Migration): 7 | dependencies = [("organizations", "0012_timestamps")] 8 | 9 | operations = [ 10 | migrations.AlterModelOptions(name="organization", options={"ordering": ("name",)}) 11 | ] 12 | -------------------------------------------------------------------------------- /apps/organizations/migrations/0014_nullable_ico.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.9 on 2020-01-10 10:03 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | dependencies = [("organizations", "0013_auto_20191113_0939")] 8 | 9 | operations = [ 10 | migrations.AlterField( 11 | model_name="organization", 12 | name="ico", 13 | field=models.PositiveIntegerField( 14 | blank=True, help_text="Business registration number", null=True 15 | ), 16 | ) 17 | ] 18 | -------------------------------------------------------------------------------- /apps/organizations/migrations/0015_nullable_ext_id.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.13 on 2020-06-25 07:56 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | dependencies = [("organizations", "0014_nullable_ico")] 8 | 9 | operations = [ 10 | migrations.AlterField( 11 | model_name="organization", 12 | name="ext_id", 13 | field=models.PositiveIntegerField( 14 | default=None, help_text="object ID taken from EMRS", null=True, unique=True 15 | ), 16 | ) 17 | ] 18 | -------------------------------------------------------------------------------- /apps/organizations/migrations/0019_jsonfield.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.1.3 on 2020-11-20 13:38 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | dependencies = [("organizations", "0018_datasource_set_null")] 8 | 9 | operations = [ 10 | migrations.AlterField( 11 | model_name="organization", 12 | name="address", 13 | field=models.JSONField(blank=True, default=dict), 14 | ) 15 | ] 16 | -------------------------------------------------------------------------------- /apps/organizations/migrations/0021_alter_organization_options.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.2.12 on 2022-03-22 14:18 2 | 3 | from django.db import migrations 4 | 5 | 6 | class Migration(migrations.Migration): 7 | dependencies = [("organizations", "0020_organization_unique_shortname")] 8 | 9 | operations = [ 10 | migrations.AlterModelOptions( 11 | name="organization", options={"ordering": ("name",), "verbose_name": "Organization"} 12 | ) 13 | ] 14 | -------------------------------------------------------------------------------- /apps/organizations/migrations/0022_organization_raw_enabled.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.2.16 on 2022-11-23 13:11 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | dependencies = [("organizations", "0021_alter_organization_options")] 8 | 9 | operations = [ 10 | migrations.AddField( 11 | model_name="organization", 12 | name="raw_data_import_enabled", 13 | field=models.BooleanField(default=False), 14 | ) 15 | ] 16 | -------------------------------------------------------------------------------- /apps/organizations/migrations/0023_alter_organizationaltname_options.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.2.18 on 2023-04-11 14:21 2 | 3 | from django.db import migrations 4 | 5 | 6 | class Migration(migrations.Migration): 7 | dependencies = [("organizations", "0022_organization_raw_enabled")] 8 | 9 | operations = [ 10 | migrations.AlterModelOptions(name="organizationaltname", options={"ordering": ["name"]}) 11 | ] 12 | -------------------------------------------------------------------------------- /apps/organizations/migrations/0024_alter_userorganization_unique_together.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.2.19 on 2023-05-16 11:10 2 | 3 | from django.conf import settings 4 | from django.db import migrations 5 | 6 | 7 | class Migration(migrations.Migration): 8 | dependencies = [ 9 | migrations.swappable_dependency(settings.AUTH_USER_MODEL), 10 | ("organizations", "0023_alter_organizationaltname_options"), 11 | ] 12 | 13 | operations = [ 14 | migrations.AlterUniqueTogether( 15 | name="userorganization", unique_together={("user", "organization")} 16 | ) 17 | ] 18 | -------------------------------------------------------------------------------- /apps/organizations/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techlib/celus/5ad1bd73a210cb5456eeea442913fa4f7649b7b5/apps/organizations/migrations/__init__.py -------------------------------------------------------------------------------- /apps/organizations/tasks.py: -------------------------------------------------------------------------------- 1 | import celery 2 | from core.context_managers import logged_task 3 | from core.logic.error_reporting import email_if_fails 4 | 5 | from organizations.logic.sync import erms_sync_organizations 6 | 7 | 8 | @celery.shared_task 9 | @logged_task 10 | @email_if_fails 11 | def erms_sync_organizations_task(): 12 | erms_sync_organizations() 13 | -------------------------------------------------------------------------------- /apps/organizations/tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techlib/celus/5ad1bd73a210cb5456eeea442913fa4f7649b7b5/apps/organizations/tests/__init__.py -------------------------------------------------------------------------------- /apps/organizations/tests/test_db.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from organizations.fake_data import OrganizationFactory 4 | 5 | from ..models import Organization 6 | 7 | 8 | @pytest.mark.django_db 9 | class TestDb: 10 | def test_organization_factory(self): 11 | assert Organization.objects.count() == 0 12 | OrganizationFactory.create() 13 | OrganizationFactory.create() 14 | assert Organization.objects.count() == 2 15 | 16 | def test_organization_str(self): 17 | org = OrganizationFactory.create() 18 | assert str(org) == org.name 19 | -------------------------------------------------------------------------------- /apps/organizations/translation.py: -------------------------------------------------------------------------------- 1 | from modeltranslation.translator import TranslationOptions, translator 2 | 3 | from .models import Organization 4 | 5 | 6 | class OrganizationTranslationOptions(TranslationOptions): 7 | fields = ("name", "short_name") 8 | 9 | 10 | translator.register(Organization, OrganizationTranslationOptions) 11 | -------------------------------------------------------------------------------- /apps/organizations/urls.py: -------------------------------------------------------------------------------- 1 | from django.urls import path 2 | from rest_framework.routers import DefaultRouter 3 | 4 | from . import views 5 | 6 | router = DefaultRouter() 7 | router.register(r"organization", views.OrganizationViewSet, basename="organization") 8 | 9 | urlpatterns = [ 10 | path("run-task/erms-sync-organizations", views.StartERMSSyncOrganizationsTask.as_view()) 11 | ] 12 | 13 | urlpatterns += router.urls 14 | -------------------------------------------------------------------------------- /apps/publications/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techlib/celus/5ad1bd73a210cb5456eeea442913fa4f7649b7b5/apps/publications/__init__.py -------------------------------------------------------------------------------- /apps/publications/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class PublicationsConfig(AppConfig): 5 | name = "publications" 6 | 7 | def ready(self): 8 | pass 9 | -------------------------------------------------------------------------------- /apps/publications/logic/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techlib/celus/5ad1bd73a210cb5456eeea442913fa4f7649b7b5/apps/publications/logic/__init__.py -------------------------------------------------------------------------------- /apps/publications/logic/knowledgebase.py: -------------------------------------------------------------------------------- 1 | import typing 2 | 3 | 4 | def get_url(knowledgebase: dict, counter_version: int) -> typing.Optional[str]: 5 | """Returns url from knowledgebase dict""" 6 | try: 7 | providers = [ 8 | e for e in knowledgebase["providers"] if e["counter_version"] == counter_version 9 | ] 10 | return providers[0]["provider"]["url"] 11 | except (KeyError, IndexError): 12 | return None 13 | -------------------------------------------------------------------------------- /apps/publications/management/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techlib/celus/5ad1bd73a210cb5456eeea442913fa4f7649b7b5/apps/publications/management/__init__.py -------------------------------------------------------------------------------- /apps/publications/management/commands/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techlib/celus/5ad1bd73a210cb5456eeea442913fa4f7649b7b5/apps/publications/management/commands/__init__.py -------------------------------------------------------------------------------- /apps/publications/management/commands/erms_sync_platforms.py: -------------------------------------------------------------------------------- 1 | from django.core.management.base import BaseCommand 2 | from django.db.transaction import atomic 3 | 4 | from publications.logic.sync import erms_sync_platforms 5 | 6 | 7 | class Command(BaseCommand): 8 | help = "Sync platforms between ERMS and the database" 9 | 10 | def add_arguments(self, parser): 11 | pass 12 | 13 | @atomic 14 | def handle(self, *args, **options): 15 | stats = erms_sync_platforms() 16 | self.stderr.write(self.style.WARNING(f"Import stats: {stats}")) 17 | -------------------------------------------------------------------------------- /apps/publications/management/commands/update_arrival_curves.py: -------------------------------------------------------------------------------- 1 | from django.core.management.base import BaseCommand 2 | from django.db.transaction import atomic 3 | 4 | from publications.logic.arrival_probabilities import update_all_arrival_curves 5 | 6 | 7 | class Command(BaseCommand): 8 | help = "Update arrival probabilities for all platforms" 9 | 10 | @atomic 11 | def handle(self, *args, **options): 12 | update_all_arrival_curves() 13 | -------------------------------------------------------------------------------- /apps/publications/migrations/0002_title_doi.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.1 on 2019-06-24 14:44 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | dependencies = [("publications", "0001_initial")] 8 | 9 | operations = [ 10 | migrations.AddField( 11 | model_name="title", name="doi", field=models.CharField(blank=True, max_length=250) 12 | ) 13 | ] 14 | -------------------------------------------------------------------------------- /apps/publications/migrations/0004_pub_type_verbose_name.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.1 on 2019-06-27 06:05 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | dependencies = [("publications", "0003_nullable_title_attrs")] 8 | 9 | operations = [ 10 | migrations.AlterField( 11 | model_name="title", 12 | name="pub_type", 13 | field=models.CharField( 14 | choices=[("B", "Book"), ("J", "Journal")], 15 | max_length=1, 16 | verbose_name="Publication type", 17 | ), 18 | ) 19 | ] 20 | -------------------------------------------------------------------------------- /apps/publications/migrations/0005_auto_20190801_1615.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.1 on 2019-08-01 16:15 2 | 3 | from django.db import migrations 4 | 5 | 6 | class Migration(migrations.Migration): 7 | dependencies = [("publications", "0004_pub_type_verbose_name")] 8 | 9 | operations = [ 10 | migrations.AlterModelOptions(name="title", options={"ordering": ("name", "pub_type")}) 11 | ] 12 | -------------------------------------------------------------------------------- /apps/publications/migrations/0007_title_unique_together.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.4 on 2019-08-02 15:53 2 | 3 | from django.db import migrations 4 | 5 | 6 | class Migration(migrations.Migration): 7 | dependencies = [("publications", "0006_no_null_title_text_attrs")] 8 | 9 | operations = [ 10 | migrations.AlterUniqueTogether( 11 | name="title", unique_together={("name", "isbn", "issn", "eissn", "doi")} 12 | ) 13 | ] 14 | -------------------------------------------------------------------------------- /apps/publications/migrations/0010_platform_interest_reports.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.4 on 2019-09-02 07:16 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | dependencies = [ 8 | ("logs", "0016_report_interest_metrics"), 9 | ("publications", "0009_even_more_pub_types"), 10 | ] 11 | 12 | operations = [ 13 | migrations.AddField( 14 | model_name="platform", 15 | name="interest_reports", 16 | field=models.ManyToManyField(to="logs.ReportType"), 17 | ) 18 | ] 19 | -------------------------------------------------------------------------------- /apps/publications/migrations/0011_unlocalize_short_name.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.4 on 2019-09-05 10:30 2 | 3 | from django.db import migrations 4 | 5 | 6 | class Migration(migrations.Migration): 7 | dependencies = [("publications", "0010_platform_interest_reports")] 8 | 9 | operations = [ 10 | migrations.AlterModelOptions(name="platform", options={"ordering": ("short_name",)}), 11 | migrations.RemoveField(model_name="platform", name="short_name_cs"), 12 | migrations.RemoveField(model_name="platform", name="short_name_en"), 13 | ] 14 | -------------------------------------------------------------------------------- /apps/publications/migrations/0014_remove_old_interest_reports_attr.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.5 on 2019-10-23 16:54 2 | 3 | from django.db import migrations 4 | 5 | 6 | class Migration(migrations.Migration): 7 | dependencies = [("publications", "0013_platforminterestreport_m2m")] 8 | 9 | operations = [ 10 | migrations.RemoveField(model_name="platform", name="interest_reports"), 11 | migrations.RenameField( 12 | model_name="platform", old_name="interest_reports_through", new_name="interest_reports" 13 | ), 14 | ] 15 | -------------------------------------------------------------------------------- /apps/publications/migrations/0018_platformtitle_unique_constraint.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.10 on 2020-04-14 13:35 2 | 3 | from django.db import migrations 4 | 5 | 6 | class Migration(migrations.Migration): 7 | dependencies = [ 8 | ("organizations", "0014_nullable_ico"), 9 | ("publications", "0017_platformtitle_unique_data"), 10 | ] 11 | 12 | operations = [ 13 | migrations.AlterUniqueTogether( 14 | name="platformtitle", unique_together={("title", "platform", "organization", "date")} 15 | ) 16 | ] 17 | -------------------------------------------------------------------------------- /apps/publications/migrations/0020_platform_nullable_ext_id.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.14 on 2020-08-12 17:31 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | dependencies = [("publications", "0019_more_pub_types")] 8 | 9 | operations = [ 10 | migrations.AlterField( 11 | model_name="platform", 12 | name="ext_id", 13 | field=models.PositiveIntegerField(blank=True, null=True, unique=True), 14 | ) 15 | ] 16 | -------------------------------------------------------------------------------- /apps/publications/migrations/0023_jsonfield.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.1.3 on 2020-11-20 13:38 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | dependencies = [("publications", "0022_datasource_set_null")] 8 | 9 | operations = [ 10 | migrations.AlterField( 11 | model_name="platform", 12 | name="knowledgebase", 13 | field=models.JSONField(blank=True, null=True), 14 | ) 15 | ] 16 | -------------------------------------------------------------------------------- /apps/publications/migrations/0028_title_uris_proprietary_ids.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.2.12 on 2022-02-24 09:00 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | dependencies = [("publications", "0027_fill_platform_name")] 8 | 9 | operations = [ 10 | migrations.AddField( 11 | model_name="title", name="proprietary_ids", field=models.JSONField(default=list) 12 | ), 13 | migrations.AddField(model_name="title", name="uris", field=models.JSONField(default=list)), 14 | ] 15 | -------------------------------------------------------------------------------- /apps/publications/migrations/0028_verbose_names.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.2.12 on 2022-03-22 14:18 2 | 3 | from django.db import migrations 4 | 5 | 6 | class Migration(migrations.Migration): 7 | dependencies = [("publications", "0027_fill_platform_name")] 8 | 9 | operations = [ 10 | migrations.AlterModelOptions( 11 | name="platform", options={"ordering": ("short_name",), "verbose_name": "Platform"} 12 | ), 13 | migrations.AlterModelOptions( 14 | name="title", 15 | options={"ordering": ("name", "pub_type"), "verbose_name": "Title/Database"}, 16 | ), 17 | ] 18 | -------------------------------------------------------------------------------- /apps/publications/migrations/0030_merge_0028_verbose_names_0029_title_default_pub_type.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.2.12 on 2022-03-25 15:27 2 | 3 | from django.db import migrations 4 | 5 | 6 | class Migration(migrations.Migration): 7 | dependencies = [ 8 | ("publications", "0028_verbose_names"), 9 | ("publications", "0029_title_default_pub_type"), 10 | ] 11 | 12 | operations = [] 13 | -------------------------------------------------------------------------------- /apps/publications/migrations/0031_alter_title_unique_together.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.2.12 on 2022-04-05 11:19 2 | 3 | from django.db import migrations 4 | 5 | 6 | class Migration(migrations.Migration): 7 | dependencies = [("publications", "0030_merge_0028_verbose_names_0029_title_default_pub_type")] 8 | 9 | operations = [ 10 | migrations.AlterUniqueTogether( 11 | name="title", 12 | unique_together={("name", "isbn", "issn", "eissn", "doi", "proprietary_ids")}, 13 | ) 14 | ] 15 | -------------------------------------------------------------------------------- /apps/publications/migrations/0040_alter_title_unique_together.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 4.2.11 on 2024-05-27 14:55 2 | 3 | from django.db import migrations 4 | 5 | 6 | class Migration(migrations.Migration): 7 | dependencies = [("publications", "0039_alter_titleoverlapbatch_last_updated_by")] 8 | 9 | operations = [migrations.AlterUniqueTogether(name="title", unique_together=set())] 10 | -------------------------------------------------------------------------------- /apps/publications/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techlib/celus/5ad1bd73a210cb5456eeea442913fa4f7649b7b5/apps/publications/migrations/__init__.py -------------------------------------------------------------------------------- /apps/publications/tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techlib/celus/5ad1bd73a210cb5456eeea442913fa4f7649b7b5/apps/publications/tests/__init__.py -------------------------------------------------------------------------------- /apps/publications/tests/test_tasks.py: -------------------------------------------------------------------------------- 1 | """ 2 | The following tests are not really about the function of the tasks, but rather to make 3 | sure that the tasks do not fail with an error. 4 | 5 | The tasks are not run through celery, but as simple functions 6 | """ 7 | 8 | import pytest 9 | 10 | from .. import tasks 11 | 12 | 13 | @pytest.mark.django_db 14 | class TestCeleryTasks: 15 | @pytest.mark.clickhouse 16 | @pytest.mark.django_db(transaction=True) 17 | def test_sync_platform_title_links_task(self, clickhouse_on_off): 18 | tasks.sync_platform_title_links_task() 19 | 20 | def test_merge_titles_task(self): 21 | tasks.merge_titles_task() 22 | -------------------------------------------------------------------------------- /apps/publications/translation.py: -------------------------------------------------------------------------------- 1 | from modeltranslation.translator import TranslationOptions, translator 2 | 3 | from .models import Platform 4 | 5 | 6 | class PlatformTranslationOptions(TranslationOptions): 7 | fields = ("name", "provider") 8 | 9 | 10 | translator.register(Platform, PlatformTranslationOptions) 11 | -------------------------------------------------------------------------------- /apps/recache/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techlib/celus/5ad1bd73a210cb5456eeea442913fa4f7649b7b5/apps/recache/__init__.py -------------------------------------------------------------------------------- /apps/recache/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class RecacheConfig(AppConfig): 5 | name = "recache" 6 | -------------------------------------------------------------------------------- /apps/recache/management/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techlib/celus/5ad1bd73a210cb5456eeea442913fa4f7649b7b5/apps/recache/management/__init__.py -------------------------------------------------------------------------------- /apps/recache/management/commands/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techlib/celus/5ad1bd73a210cb5456eeea442913fa4f7649b7b5/apps/recache/management/commands/__init__.py -------------------------------------------------------------------------------- /apps/recache/migrations/0003_cachedquery_origin.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.15 on 2020-08-24 08:02 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | dependencies = [("recache", "0002_add_stats")] 8 | 9 | operations = [ 10 | migrations.AddField( 11 | model_name="cachedquery", 12 | name="origin", 13 | field=models.CharField( 14 | blank=True, help_text="Optional identifier of the query's origin", max_length=32 15 | ), 16 | ) 17 | ] 18 | -------------------------------------------------------------------------------- /apps/recache/migrations/0005_remove_cachedquery_query_pickle.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.15 on 2020-08-31 09:09 2 | 3 | from django.db import migrations 4 | 5 | 6 | class Migration(migrations.Migration): 7 | dependencies = [("recache", "0004_unique_query_hash")] 8 | 9 | operations = [migrations.RemoveField(model_name="cachedquery", name="query_pickle")] 10 | -------------------------------------------------------------------------------- /apps/recache/migrations/0006_query_hash_unique_with_django_version.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.15 on 2020-09-14 11:50 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | dependencies = [("recache", "0005_remove_cachedquery_query_pickle")] 8 | 9 | operations = [ 10 | migrations.AlterField( 11 | model_name="cachedquery", 12 | name="query_hash", 13 | field=models.CharField(help_text="Hash of the query string", max_length=32), 14 | ), 15 | migrations.AlterUniqueTogether( 16 | name="cachedquery", unique_together={("query_hash", "django_version")} 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /apps/recache/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techlib/celus/5ad1bd73a210cb5456eeea442913fa4f7649b7b5/apps/recache/migrations/__init__.py -------------------------------------------------------------------------------- /apps/recache/tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techlib/celus/5ad1bd73a210cb5456eeea442913fa4f7649b7b5/apps/recache/tests/__init__.py -------------------------------------------------------------------------------- /apps/recache/views.py: -------------------------------------------------------------------------------- 1 | # Create your views here. 2 | -------------------------------------------------------------------------------- /apps/releases/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techlib/celus/5ad1bd73a210cb5456eeea442913fa4f7649b7b5/apps/releases/__init__.py -------------------------------------------------------------------------------- /apps/releases/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class ReleasesConfig(AppConfig): 5 | name = "releases" 6 | -------------------------------------------------------------------------------- /apps/releases/logic/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techlib/celus/5ad1bd73a210cb5456eeea442913fa4f7649b7b5/apps/releases/logic/__init__.py -------------------------------------------------------------------------------- /apps/releases/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techlib/celus/5ad1bd73a210cb5456eeea442913fa4f7649b7b5/apps/releases/migrations/__init__.py -------------------------------------------------------------------------------- /apps/releases/tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techlib/celus/5ad1bd73a210cb5456eeea442913fa4f7649b7b5/apps/releases/tests/__init__.py -------------------------------------------------------------------------------- /apps/releases/urls.py: -------------------------------------------------------------------------------- 1 | from django.urls import path 2 | from rest_framework.routers import DefaultRouter 3 | 4 | from . import views 5 | 6 | router = DefaultRouter() 7 | router.register(r"releases", views.Releases, basename="releases") 8 | 9 | urlpatterns = [path("changelog/", views.ChangelogAPIView.as_view(), name="changelog")] + router.urls 10 | -------------------------------------------------------------------------------- /apps/reporting/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | This module is intended for specialized reports which do not fit into the generic reporting 3 | provided in `logs`. 4 | """ 5 | 6 | # TODO: remove this comment when the corresponding code from `logs` is moved here. 7 | # It is possible that in the future, we will move some of the code from `logs` to this module. 8 | # It is also possible that a more generic reporting framework will be developed, based on the 9 | # more ad-hoc code in this module. 10 | -------------------------------------------------------------------------------- /apps/reporting/admin.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techlib/celus/5ad1bd73a210cb5456eeea442913fa4f7649b7b5/apps/reporting/admin.py -------------------------------------------------------------------------------- /apps/reporting/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class ReportingConfig(AppConfig): 5 | default_auto_field = "django.db.models.BigAutoField" 6 | name = "reporting" 7 | -------------------------------------------------------------------------------- /apps/reporting/logic/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techlib/celus/5ad1bd73a210cb5456eeea442913fa4f7649b7b5/apps/reporting/logic/__init__.py -------------------------------------------------------------------------------- /apps/reporting/models.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techlib/celus/5ad1bd73a210cb5456eeea442913fa4f7649b7b5/apps/reporting/models.py -------------------------------------------------------------------------------- /apps/reporting/tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techlib/celus/5ad1bd73a210cb5456eeea442913fa4f7649b7b5/apps/reporting/tests/__init__.py -------------------------------------------------------------------------------- /apps/reporting/urls.py: -------------------------------------------------------------------------------- 1 | from django.urls import path 2 | 3 | from . import views 4 | 5 | urlpatterns = [ 6 | path("reporting/reports/", views.ReportListView.as_view(), name="report-list"), 7 | path( 8 | "reporting/reports//", views.ReportDataView.as_view(), name="report-data" 9 | ), 10 | path( 11 | "reporting/reports//export/", 12 | views.ReportExportView.as_view(), 13 | name="report-export", 14 | ), 15 | ] 16 | -------------------------------------------------------------------------------- /apps/scheduler/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techlib/celus/5ad1bd73a210cb5456eeea442913fa4f7649b7b5/apps/scheduler/__init__.py -------------------------------------------------------------------------------- /apps/scheduler/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class SchedulerConfig(AppConfig): 5 | name = "scheduler" 6 | 7 | def ready(self): 8 | from . import signals # noqa 9 | -------------------------------------------------------------------------------- /apps/scheduler/filters/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techlib/celus/5ad1bd73a210cb5456eeea442913fa4f7649b7b5/apps/scheduler/filters/__init__.py -------------------------------------------------------------------------------- /apps/scheduler/logic/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techlib/celus/5ad1bd73a210cb5456eeea442913fa4f7649b7b5/apps/scheduler/logic/__init__.py -------------------------------------------------------------------------------- /apps/scheduler/migrations/0005_fetchintention_date_constraint.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.16 on 2020-10-27 15:48 2 | 3 | import django.db.models.expressions 4 | from django.db import migrations, models 5 | 6 | 7 | class Migration(migrations.Migration): 8 | dependencies = [("scheduler", "0004_automatic")] 9 | 10 | operations = [ 11 | migrations.AddConstraint( 12 | model_name="fetchintention", 13 | constraint=models.CheckConstraint( 14 | check=models.Q(start_date__lt=django.db.models.expressions.F("end_date")), 15 | name="timeline", 16 | ), 17 | ) 18 | ] 19 | -------------------------------------------------------------------------------- /apps/scheduler/migrations/0006_fetchintention_retry_id.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.16 on 2020-10-30 09:43 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | dependencies = [("scheduler", "0005_fetchintention_date_constraint")] 8 | 9 | operations = [ 10 | migrations.AddField( 11 | model_name="fetchintention", 12 | name="retry_id", 13 | field=models.IntegerField(blank=True, help_text="Identifier of retry queue", null=True), 14 | ) 15 | ] 16 | -------------------------------------------------------------------------------- /apps/scheduler/migrations/0007_fetchintention_one_to_one_attempt.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.16 on 2020-10-30 12:58 2 | 3 | import django.db.models.deletion 4 | from django.db import migrations, models 5 | 6 | 7 | class Migration(migrations.Migration): 8 | dependencies = [("scheduler", "0006_fetchintention_retry_id")] 9 | 10 | operations = [ 11 | migrations.AlterField( 12 | model_name="fetchintention", 13 | name="attempt", 14 | field=models.OneToOneField( 15 | null=True, 16 | on_delete=django.db.models.deletion.SET_NULL, 17 | to="sushi.SushiFetchAttempt", 18 | ), 19 | ) 20 | ] 21 | -------------------------------------------------------------------------------- /apps/scheduler/migrations/0010_fetchintention_canceled.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.1.8 on 2021-05-19 08:37 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | dependencies = [("scheduler", "0009_rename_retry_id_to_queue_id")] 8 | 9 | operations = [ 10 | migrations.AddField( 11 | model_name="fetchintention", name="canceled", field=models.BooleanField(default=False) 12 | ) 13 | ] 14 | -------------------------------------------------------------------------------- /apps/scheduler/migrations/0012_fill_queue_id.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.2.8 on 2021-10-25 16:08 2 | 3 | from django.db import migrations 4 | from django.db.migrations import RunPython 5 | from django.db.models import F 6 | 7 | 8 | def add_queue_id(apps, schema_editor): 9 | FetchIntention = apps.get_model("scheduler", "FetchIntention") 10 | FetchIntention.objects.filter(queue_id__isnull=True).update(queue_id=F("pk")) 11 | assert FetchIntention.objects.filter(queue_id__isnull=True).count() == 0 12 | 13 | 14 | class Migration(migrations.Migration): 15 | dependencies = [("scheduler", "0011_fetchintention_previous")] 16 | 17 | operations = [RunPython(add_queue_id, RunPython.noop)] 18 | -------------------------------------------------------------------------------- /apps/scheduler/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techlib/celus/5ad1bd73a210cb5456eeea442913fa4f7649b7b5/apps/scheduler/migrations/__init__.py -------------------------------------------------------------------------------- /apps/scheduler/tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techlib/celus/5ad1bd73a210cb5456eeea442913fa4f7649b7b5/apps/scheduler/tests/__init__.py -------------------------------------------------------------------------------- /apps/scheduler/tests/conftest.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techlib/celus/5ad1bd73a210cb5456eeea442913fa4f7649b7b5/apps/scheduler/tests/conftest.py -------------------------------------------------------------------------------- /apps/scheduler/urls.py: -------------------------------------------------------------------------------- 1 | from rest_framework.routers import SimpleRouter 2 | from rest_framework_nested.routers import NestedSimpleRouter 3 | 4 | from .views import HarvestIntentionViewSet, HarvestViewSet, IntentionViewSet 5 | 6 | router = SimpleRouter() 7 | router.register("harvest", HarvestViewSet, basename="harvest") 8 | router.register("intention", IntentionViewSet, basename="intention") 9 | 10 | harvest_router = NestedSimpleRouter(router, "harvest", lookup="harvest") 11 | harvest_router.register("intention", HarvestIntentionViewSet, basename="harvest-intention") 12 | 13 | urlpatterns = [*router.urls, *harvest_router.urls] 14 | -------------------------------------------------------------------------------- /apps/sushi/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techlib/celus/5ad1bd73a210cb5456eeea442913fa4f7649b7b5/apps/sushi/__init__.py -------------------------------------------------------------------------------- /apps/sushi/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class SushiConfig(AppConfig): 5 | name = "sushi" 6 | -------------------------------------------------------------------------------- /apps/sushi/logic/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techlib/celus/5ad1bd73a210cb5456eeea442913fa4f7649b7b5/apps/sushi/logic/__init__.py -------------------------------------------------------------------------------- /apps/sushi/management/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techlib/celus/5ad1bd73a210cb5456eeea442913fa4f7649b7b5/apps/sushi/management/__init__.py -------------------------------------------------------------------------------- /apps/sushi/management/commands/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techlib/celus/5ad1bd73a210cb5456eeea442913fa4f7649b7b5/apps/sushi/management/commands/__init__.py -------------------------------------------------------------------------------- /apps/sushi/migrations/0003_counterreporttype_active.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.4 on 2019-08-02 14:48 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | dependencies = [("sushi", "0002_sushifetchattempt")] 8 | 9 | operations = [ 10 | migrations.AddField( 11 | model_name="counterreporttype", 12 | name="active", 13 | field=models.BooleanField( 14 | default=True, 15 | help_text="When turned off, this type of report will not be automatically " 16 | "downloaded", 17 | ), 18 | ) 19 | ] 20 | -------------------------------------------------------------------------------- /apps/sushi/migrations/0006_sushicredentials_active_counter_reports.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.1 on 2019-08-08 15:33 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | dependencies = [("sushi", "0005_attemp_queuing")] 8 | 9 | operations = [ 10 | migrations.AddField( 11 | model_name="sushicredentials", 12 | name="active_counter_reports", 13 | field=models.ManyToManyField(to="sushi.CounterReportType"), 14 | ) 15 | ] 16 | -------------------------------------------------------------------------------- /apps/sushi/migrations/0008_sushiattemp_importbatch_onetoone.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.4 on 2019-08-16 08:33 2 | 3 | import django.db.models.deletion 4 | from django.db import migrations, models 5 | 6 | 7 | class Migration(migrations.Migration): 8 | dependencies = [("sushi", "0007_sushifetchattempt_import_batch")] 9 | 10 | operations = [ 11 | migrations.AlterField( 12 | model_name="sushifetchattempt", 13 | name="import_batch", 14 | field=models.OneToOneField( 15 | null=True, on_delete=django.db.models.deletion.SET_NULL, to="logs.ImportBatch" 16 | ), 17 | ) 18 | ] 19 | -------------------------------------------------------------------------------- /apps/sushi/migrations/0009_conterreporttype_oneonone_report_type.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.4 on 2019-08-25 07:34 2 | 3 | import django.db.models.deletion 4 | from django.db import migrations, models 5 | 6 | 7 | class Migration(migrations.Migration): 8 | dependencies = [("sushi", "0008_sushiattemp_importbatch_onetoone")] 9 | 10 | operations = [ 11 | migrations.AlterField( 12 | model_name="counterreporttype", 13 | name="report_type", 14 | field=models.OneToOneField( 15 | on_delete=django.db.models.deletion.CASCADE, to="logs.ReportType" 16 | ), 17 | ) 18 | ] 19 | -------------------------------------------------------------------------------- /apps/sushi/migrations/0011_sushifetchattempt_error_code.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.4 on 2019-08-28 07:24 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | dependencies = [("sushi", "0010_counterreporttype_code_choices")] 8 | 9 | operations = [ 10 | migrations.AddField( 11 | model_name="sushifetchattempt", 12 | name="error_code", 13 | field=models.CharField(blank=True, max_length=12), 14 | ) 15 | ] 16 | -------------------------------------------------------------------------------- /apps/sushi/migrations/0012_sushifetchattempt_contains_data.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.4 on 2019-08-28 12:08 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | dependencies = [("sushi", "0011_sushifetchattempt_error_code")] 8 | 9 | operations = [ 10 | migrations.AddField( 11 | model_name="sushifetchattempt", 12 | name="contains_data", 13 | field=models.BooleanField( 14 | default=False, help_text="Does the report actually contain data for import" 15 | ), 16 | ) 17 | ] 18 | -------------------------------------------------------------------------------- /apps/sushi/migrations/0017_sushifetchattempt_processing_info.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.4 on 2019-09-13 08:54 2 | 3 | import django.contrib.postgres.fields.jsonb 4 | from django.db import migrations 5 | 6 | 7 | class Migration(migrations.Migration): 8 | dependencies = [("sushi", "0016_counterreporttype_superseeded_by")] 9 | 10 | operations = [ 11 | migrations.AddField( 12 | model_name="sushifetchattempt", 13 | name="processing_info", 14 | field=django.contrib.postgres.fields.jsonb.JSONField( 15 | default=dict, help_text="Internal info" 16 | ), 17 | ) 18 | ] 19 | -------------------------------------------------------------------------------- /apps/sushi/migrations/0018_sushifetchattempt_in_progress.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.5 on 2019-09-16 08:52 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | dependencies = [("sushi", "0017_sushifetchattempt_processing_info")] 8 | 9 | operations = [ 10 | migrations.AddField( 11 | model_name="sushifetchattempt", 12 | name="in_progress", 13 | field=models.BooleanField( 14 | default=False, help_text="True if the data is still downloading" 15 | ), 16 | ) 17 | ] 18 | -------------------------------------------------------------------------------- /apps/sushi/migrations/0021_sushifetchattempt_import_crashed.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.5 on 2019-09-20 07:52 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | dependencies = [("sushi", "0020_remove_JR5_counter_report")] 8 | 9 | operations = [ 10 | migrations.AddField( 11 | model_name="sushifetchattempt", 12 | name="import_crashed", 13 | field=models.BooleanField( 14 | default=False, 15 | help_text="Set to true if there was an error during data import. Details in log " 16 | "and processing_info", 17 | ), 18 | ) 19 | ] 20 | -------------------------------------------------------------------------------- /apps/sushi/migrations/0025_remove_counterreporttype_superseeded_by.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.5 on 2019-11-06 10:07 2 | 3 | from django.db import migrations 4 | 5 | 6 | class Migration(migrations.Migration): 7 | # dependency on 0028_reporttype_superseeded_by was added ex-post when I found out that 8 | # in some cases the migration order was incorrect and this change occurred before 9 | # 0028_reporttype_superseeded_by which is incorrect 10 | dependencies = [("sushi", "0024_lock_level_change"), ("logs", "0028_reporttype_superseeded_by")] 11 | 12 | operations = [migrations.RemoveField(model_name="counterreporttype", name="superseeded_by")] 13 | -------------------------------------------------------------------------------- /apps/sushi/migrations/0031_sushifetchattempt_last_updated.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.14 on 2020-08-17 09:54 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | dependencies = [("sushi", "0030_sushicredentials_title")] 8 | 9 | operations = [ 10 | migrations.AddField( 11 | model_name="sushifetchattempt", 12 | name="last_updated", 13 | field=models.DateTimeField(auto_now=True), 14 | ) 15 | ] 16 | -------------------------------------------------------------------------------- /apps/sushi/migrations/0034_longer_data_file_field.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.15 on 2020-09-07 17:38 2 | 3 | from django.db import migrations, models 4 | 5 | import sushi.models 6 | 7 | 8 | class Migration(migrations.Migration): 9 | dependencies = [("sushi", "0033_sushifetchattempt_queue_id")] 10 | 11 | operations = [ 12 | migrations.AlterField( 13 | model_name="sushifetchattempt", 14 | name="data_file", 15 | field=models.FileField( 16 | blank=True, max_length=256, null=True, upload_to=sushi.models.where_to_store 17 | ), 18 | ) 19 | ] 20 | -------------------------------------------------------------------------------- /apps/sushi/migrations/0035_sushifetchattempt_http_status_code.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.15 on 2020-09-11 14:02 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | dependencies = [("sushi", "0034_longer_data_file_field")] 8 | 9 | operations = [ 10 | migrations.AddField( 11 | model_name="sushifetchattempt", 12 | name="http_status_code", 13 | field=models.PositiveSmallIntegerField(null=True), 14 | ) 15 | ] 16 | -------------------------------------------------------------------------------- /apps/sushi/migrations/0039_remove_http_auth_from_c5.py: -------------------------------------------------------------------------------- 1 | from django.db import migrations 2 | 3 | 4 | def remove_http_auth_from_c5(apps, schema_editor): 5 | SushiCredentials = apps.get_model("sushi", "SushiCredentials") 6 | SushiCredentials.objects.filter(counter_version=5).update(http_password="", http_username="") 7 | 8 | 9 | def noop(apps, schema_editor): 10 | pass 11 | 12 | 13 | class Migration(migrations.Migration): 14 | dependencies = [("sushi", "0038_jsonfield")] 15 | 16 | operations = [migrations.RunPython(remove_http_auth_from_c5, noop)] 17 | -------------------------------------------------------------------------------- /apps/sushi/migrations/0043_sushifetchattempt_partial_data.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.1.8 on 2021-05-11 13:15 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | dependencies = [("sushi", "0042_counterreporttype_code_update")] 8 | 9 | operations = [ 10 | migrations.AddField( 11 | model_name="sushifetchattempt", 12 | name="partial_data", 13 | field=models.BooleanField(default=False, help_text="Data may not be complete"), 14 | ) 15 | ] 16 | -------------------------------------------------------------------------------- /apps/sushi/migrations/0049_sushifetchattempt_extracted_data.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.2.12 on 2022-02-22 16:13 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | dependencies = [("sushi", "0048_discard_credentials_broken_state")] 8 | 9 | operations = [ 10 | migrations.AddField( 11 | model_name="sushifetchattempt", 12 | name="extracted_data", 13 | field=models.JSONField( 14 | default=dict, help_text="Information extracted from the SUSHI data header" 15 | ), 16 | ) 17 | ] 18 | -------------------------------------------------------------------------------- /apps/sushi/migrations/0050_alter_sushicredentials_api_key.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.2.12 on 2022-04-11 07:50 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | dependencies = [("sushi", "0049_sushifetchattempt_extracted_data")] 8 | 9 | operations = [ 10 | migrations.AlterField( 11 | model_name="sushicredentials", 12 | name="api_key", 13 | field=models.CharField(blank=True, max_length=400), 14 | ) 15 | ] 16 | -------------------------------------------------------------------------------- /apps/sushi/migrations/0051_fetchattempt_remove_queue_stuff.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.2.12 on 2022-04-27 11:43 2 | 3 | from django.db import migrations 4 | 5 | 6 | class Migration(migrations.Migration): 7 | dependencies = [("sushi", "0050_alter_sushicredentials_api_key")] 8 | 9 | operations = [ 10 | migrations.RemoveField(model_name="sushifetchattempt", name="queue_id"), 11 | migrations.RemoveField(model_name="sushifetchattempt", name="queue_previous"), 12 | migrations.RemoveField(model_name="sushifetchattempt", name="when_queued"), 13 | ] 14 | -------------------------------------------------------------------------------- /apps/sushi/migrations/0056_alter_sushifetchattempt_credentials.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.2.18 on 2023-05-03 12:32 2 | 3 | import django.db.models.deletion 4 | from django.db import migrations, models 5 | 6 | 7 | class Migration(migrations.Migration): 8 | dependencies = [("sushi", "0055_fill_missing_extracted_data")] 9 | 10 | operations = [ 11 | migrations.AlterField( 12 | model_name="sushifetchattempt", 13 | name="credentials", 14 | field=models.ForeignKey( 15 | null=True, on_delete=django.db.models.deletion.SET_NULL, to="sushi.sushicredentials" 16 | ), 17 | ) 18 | ] 19 | -------------------------------------------------------------------------------- /apps/sushi/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techlib/celus/5ad1bd73a210cb5456eeea442913fa4f7649b7b5/apps/sushi/migrations/__init__.py -------------------------------------------------------------------------------- /apps/sushi/static/admin/admin.css: -------------------------------------------------------------------------------- 1 | #result_list th span,a {white-space: normal;} 2 | -------------------------------------------------------------------------------- /apps/sushi/tasks.py: -------------------------------------------------------------------------------- 1 | import celery 2 | from core.context_managers import logged_task 3 | from core.logic.error_reporting import email_if_fails 4 | 5 | from sushi.logic.cleanup import delete_fetchattempts_and_related_importbatches 6 | 7 | 8 | @celery.shared_task 9 | @logged_task 10 | @email_if_fails 11 | def delete_fetchattempts_and_related_importbatches_task(fetch_attempts_pks: list): 12 | delete_fetchattempts_and_related_importbatches(fetch_attempts_pks) 13 | -------------------------------------------------------------------------------- /apps/sushi/templates/Template_for_SushiCredentials_import_consortium.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techlib/celus/5ad1bd73a210cb5456eeea442913fa4f7649b7b5/apps/sushi/templates/Template_for_SushiCredentials_import_consortium.xlsx -------------------------------------------------------------------------------- /apps/sushi/templates/Template_for_SushiCredentials_import_singleorg.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techlib/celus/5ad1bd73a210cb5456eeea442913fa4f7649b7b5/apps/sushi/templates/Template_for_SushiCredentials_import_singleorg.xlsx -------------------------------------------------------------------------------- /apps/sushi/tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techlib/celus/5ad1bd73a210cb5456eeea442913fa4f7649b7b5/apps/sushi/tests/__init__.py -------------------------------------------------------------------------------- /apps/sushi/tests/data: -------------------------------------------------------------------------------- 1 | ../../../test-data/ -------------------------------------------------------------------------------- /apps/sushi/urls.py: -------------------------------------------------------------------------------- 1 | from rest_framework.routers import SimpleRouter 2 | 3 | from . import views 4 | 5 | router = SimpleRouter() 6 | router.register(r"sushi-credentials", views.SushiCredentialsViewSet, basename="sushi-credentials") 7 | router.register(r"counter-report-type", views.CounterReportTypeViewSet) 8 | 9 | urlpatterns = router.urls 10 | -------------------------------------------------------------------------------- /apps/tags/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techlib/celus/5ad1bd73a210cb5456eeea442913fa4f7649b7b5/apps/tags/__init__.py -------------------------------------------------------------------------------- /apps/tags/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class TagsConfig(AppConfig): 5 | default_auto_field = "django.db.models.BigAutoField" 6 | name = "tags" 7 | -------------------------------------------------------------------------------- /apps/tags/filters.py: -------------------------------------------------------------------------------- 1 | from rest_framework import filters 2 | 3 | from tags.models import TagScope 4 | 5 | 6 | class TagClassScopeFilter(filters.BaseFilterBackend): 7 | """ 8 | Filter that allows selection of tag scope 9 | """ 10 | 11 | filter_field = "scope" 12 | query_param = "scope" 13 | 14 | def filter_queryset(self, request, queryset, view): 15 | scope = request.query_params.get(self.query_param) 16 | if scope and scope in TagScope.values: 17 | queryset = queryset.filter(**{f"{self.filter_field}": scope}) 18 | return queryset 19 | -------------------------------------------------------------------------------- /apps/tags/logic/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techlib/celus/5ad1bd73a210cb5456eeea442913fa4f7649b7b5/apps/tags/logic/__init__.py -------------------------------------------------------------------------------- /apps/tags/management/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techlib/celus/5ad1bd73a210cb5456eeea442913fa4f7649b7b5/apps/tags/management/__init__.py -------------------------------------------------------------------------------- /apps/tags/management/commands/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techlib/celus/5ad1bd73a210cb5456eeea442913fa4f7649b7b5/apps/tags/management/commands/__init__.py -------------------------------------------------------------------------------- /apps/tags/migrations/0005_alter_tag_options.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.2.15 on 2022-08-30 13:38 2 | 3 | from django.db import migrations 4 | 5 | 6 | class Migration(migrations.Migration): 7 | dependencies = [("tags", "0004_alter_taggingbatch_state")] 8 | 9 | operations = [ 10 | migrations.AlterModelOptions( 11 | name="tag", options={"ordering": ["name"], "verbose_name": "Tag"} 12 | ) 13 | ] 14 | -------------------------------------------------------------------------------- /apps/tags/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techlib/celus/5ad1bd73a210cb5456eeea442913fa4f7649b7b5/apps/tags/migrations/__init__.py -------------------------------------------------------------------------------- /apps/tags/tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techlib/celus/5ad1bd73a210cb5456eeea442913fa4f7649b7b5/apps/tags/tests/__init__.py -------------------------------------------------------------------------------- /config/__init__.py: -------------------------------------------------------------------------------- 1 | # This will make sure the app is always imported when 2 | # Django starts so that shared_task will use this app. 3 | from .celery_conf import app as celery_app 4 | 5 | __all__ = ["celery_app"] 6 | -------------------------------------------------------------------------------- /config/celery_conf.py: -------------------------------------------------------------------------------- 1 | from celery import Celery 2 | 3 | app = Celery("celus") 4 | 5 | app.config_from_object("django.conf:settings", namespace="CELERY") 6 | 7 | # Load task modules from all registered Django app configs. 8 | app.autodiscover_tasks() 9 | 10 | 11 | @app.task(bind=True) 12 | def debug_task(self): 13 | print("Request: {0!r}".format(self.request)) 14 | -------------------------------------------------------------------------------- /config/production.wsgi: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | from django.core.wsgi import get_wsgi_application 4 | 5 | os.environ["DJANGO_SETTINGS_MODULE"] = "config.settings.production" 6 | os.environ["prometheus_multiproc_dir"] = "/tmp/django_prometheus/" 7 | 8 | application = get_wsgi_application() 9 | -------------------------------------------------------------------------------- /config/settings/__init__.py: -------------------------------------------------------------------------------- 1 | import re 2 | from pathlib import Path 3 | 4 | 5 | def get_version(base_dir: Path) -> str: 6 | try: 7 | with (base_dir / "pyproject.toml").open() as f: 8 | for line in f.readlines(): 9 | match = re.match(r'^version\s+=\s+"([^"]+)"$', line.strip()) 10 | if match: 11 | return match.group(1) 12 | 13 | except Exception as e: 14 | raise RuntimeError("Failed to read pyproject.toml") from e 15 | 16 | raise RuntimeError("Version not found in pyproject.toml") 17 | -------------------------------------------------------------------------------- /config/settings/production.py: -------------------------------------------------------------------------------- 1 | from .base import * # noqa F403 F401 2 | -------------------------------------------------------------------------------- /config/settings/staging.py: -------------------------------------------------------------------------------- 1 | from .base import * # noqa F403 F401 2 | 3 | ALLOWED_HOSTS = ["stats.test.czechelib.cz"] 4 | LIVE_ERMS_AUTHENTICATION = True 5 | 6 | DEBUG = False 7 | 8 | STATIC_ROOT = "/var/www/celus/static/" 9 | MEDIA_ROOT = "/var/www/celus/media/" 10 | 11 | ADMINS = (("Beda Kosata", "beda@bigdigdata.com"),) 12 | 13 | EMAIL_HOST = "smtp.ntkcz.cz" 14 | SERVER_EMAIL = "admin@stats.test.czechelib.cz" 15 | -------------------------------------------------------------------------------- /config/staging.wsgi: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | from django.core.wsgi import get_wsgi_application 4 | 5 | os.environ["DJANGO_SETTINGS_MODULE"] = "config.settings.staging" 6 | os.environ["prometheus_multiproc_dir"] = "/tmp/django_prometheus/" 7 | 8 | application = get_wsgi_application() 9 | -------------------------------------------------------------------------------- /config/wsgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | WSGI config for Celus project. 3 | 4 | It exposes the WSGI callable as a module-level variable named ``application``. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/2.2/howto/deployment/wsgi/ 8 | """ 9 | 10 | import os 11 | 12 | from django.core.wsgi import get_wsgi_application 13 | 14 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "config.settings.production") 15 | 16 | application = get_wsgi_application() 17 | -------------------------------------------------------------------------------- /design/ui/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["@babel/preset-env"] 3 | } 4 | -------------------------------------------------------------------------------- /design/ui/.env: -------------------------------------------------------------------------------- 1 | VUE_APP_I18N_LOCALE=en 2 | VUE_APP_I18N_FALLBACK_LOCALE=en 3 | -------------------------------------------------------------------------------- /design/ui/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /dist 4 | 5 | # local env files 6 | .env.local 7 | .env.*.local 8 | 9 | # Log files 10 | npm-debug.log* 11 | yarn-debug.log* 12 | yarn-error.log* 13 | 14 | # Editor directories and files 15 | .idea 16 | .vscode 17 | *.suo 18 | *.ntvs* 19 | *.njsproj 20 | *.sln 21 | *.sw? 22 | -------------------------------------------------------------------------------- /design/ui/README.md: -------------------------------------------------------------------------------- 1 | # ui 2 | 3 | ## Project setup 4 | 5 | ``` 6 | yarn install 7 | ``` 8 | 9 | ### Compiles and hot-reloads for development 10 | 11 | ``` 12 | yarn serve 13 | ``` 14 | 15 | ### Compiles and minifies for production 16 | 17 | ``` 18 | yarn build 19 | ``` 20 | 21 | ### Run your tests 22 | 23 | ``` 24 | yarn test 25 | ``` 26 | 27 | ### Lints and fixes files 28 | 29 | ``` 30 | yarn lint 31 | ``` 32 | 33 | ### Customize configuration 34 | 35 | See [Configuration Reference](https://cli.vuejs.org/config/). 36 | -------------------------------------------------------------------------------- /design/ui/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: ["@vue/app", "@babel/preset-env"], 3 | plugins: ["@babel/plugin-transform-class-properties"], 4 | }; 5 | -------------------------------------------------------------------------------- /design/ui/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techlib/celus/5ad1bd73a210cb5456eeea442913fa4f7649b7b5/design/ui/public/favicon.ico -------------------------------------------------------------------------------- /design/ui/public/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techlib/celus/5ad1bd73a210cb5456eeea442913fa4f7649b7b5/design/ui/public/favicon.png -------------------------------------------------------------------------------- /design/ui/src/assets/celus-dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techlib/celus/5ad1bd73a210cb5456eeea442913fa4f7649b7b5/design/ui/src/assets/celus-dark.png -------------------------------------------------------------------------------- /design/ui/src/assets/czechelib_logo-mini_color-transp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techlib/celus/5ad1bd73a210cb5456eeea442913fa4f7649b7b5/design/ui/src/assets/czechelib_logo-mini_color-transp.png -------------------------------------------------------------------------------- /design/ui/src/assets/eu-96.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techlib/celus/5ad1bd73a210cb5456eeea442913fa4f7649b7b5/design/ui/src/assets/eu-96.png -------------------------------------------------------------------------------- /design/ui/src/assets/eu-large.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techlib/celus/5ad1bd73a210cb5456eeea442913fa4f7649b7b5/design/ui/src/assets/eu-large.png -------------------------------------------------------------------------------- /design/ui/src/assets/eu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techlib/celus/5ad1bd73a210cb5456eeea442913fa4f7649b7b5/design/ui/src/assets/eu.png -------------------------------------------------------------------------------- /design/ui/src/assets/ex-metric.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techlib/celus/5ad1bd73a210cb5456eeea442913fa4f7649b7b5/design/ui/src/assets/ex-metric.png -------------------------------------------------------------------------------- /design/ui/src/assets/ex-title-metric-publisher-success.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techlib/celus/5ad1bd73a210cb5456eeea442913fa4f7649b7b5/design/ui/src/assets/ex-title-metric-publisher-success.png -------------------------------------------------------------------------------- /design/ui/src/assets/ex-title.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techlib/celus/5ad1bd73a210cb5456eeea442913fa4f7649b7b5/design/ui/src/assets/ex-title.png -------------------------------------------------------------------------------- /design/ui/src/assets/hungry-celus-240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techlib/celus/5ad1bd73a210cb5456eeea442913fa4f7649b7b5/design/ui/src/assets/hungry-celus-240.png -------------------------------------------------------------------------------- /design/ui/src/assets/hungry-celus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techlib/celus/5ad1bd73a210cb5456eeea442913fa4f7649b7b5/design/ui/src/assets/hungry-celus.png -------------------------------------------------------------------------------- /design/ui/src/assets/sushi_credentials-enable.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techlib/celus/5ad1bd73a210cb5456eeea442913fa4f7649b7b5/design/ui/src/assets/sushi_credentials-enable.png -------------------------------------------------------------------------------- /design/ui/src/assets/sushi_credentials-test.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techlib/celus/5ad1bd73a210cb5456eeea442913fa4f7649b7b5/design/ui/src/assets/sushi_credentials-test.png -------------------------------------------------------------------------------- /design/ui/src/assets/sushi_credentials.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techlib/celus/5ad1bd73a210cb5456eeea442913fa4f7649b7b5/design/ui/src/assets/sushi_credentials.png -------------------------------------------------------------------------------- /design/ui/src/components/HoverText.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 33 | 34 | 39 | -------------------------------------------------------------------------------- /design/ui/src/components/events/EventCategoryMark.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 13 | 14 | 26 | -------------------------------------------------------------------------------- /design/ui/src/components/reporting/MetricChip.vue: -------------------------------------------------------------------------------- 1 | 13 | 14 | 29 | -------------------------------------------------------------------------------- /design/ui/src/components/reporting/TrendArrow.vue: -------------------------------------------------------------------------------- 1 | 12 | 20 | -------------------------------------------------------------------------------- /design/ui/src/components/sushi/HarvesterIPAddressList.vue: -------------------------------------------------------------------------------- 1 | 11 | 25 | -------------------------------------------------------------------------------- /design/ui/src/components/sushi/IconButton.vue: -------------------------------------------------------------------------------- 1 | 11 | 20 | 25 | -------------------------------------------------------------------------------- /design/ui/src/components/sushi/RegistryIcon.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 14 | 26 | -------------------------------------------------------------------------------- /design/ui/src/components/util/DoiLink.vue: -------------------------------------------------------------------------------- 1 | 14 | 15 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /design/ui/src/components/util/LargeSpinner.vue: -------------------------------------------------------------------------------- 1 | 9 | 14 | -------------------------------------------------------------------------------- /design/ui/src/components/util/SmallLoader.vue: -------------------------------------------------------------------------------- 1 | 9 | 19 | -------------------------------------------------------------------------------- /design/ui/src/libs/charts.js: -------------------------------------------------------------------------------- 1 | export const DEFAULT_VCHARTS_COLORS = [ 2 | "#19d4ae", 3 | "#5ab1ef", 4 | "#fa6e86", 5 | "#ffb980", 6 | "#0067a6", 7 | "#c4b4e4", 8 | "#d87a80", 9 | "#9cbbff", 10 | "#d9d0c7", 11 | "#87a997", 12 | "#d49ea2", 13 | "#5b4947", 14 | "#7ba3a8", 15 | ]; 16 | -------------------------------------------------------------------------------- /design/ui/src/libs/db-object-localization.js: -------------------------------------------------------------------------------- 1 | function itemToString(item, locale = "en") { 2 | if (item !== null) { 3 | for (let prefix of ["text_local_", "name_"]) { 4 | let key = `${prefix}${locale}`; 5 | if (item[key]) { 6 | return item[key]; 7 | } 8 | } 9 | let result = 10 | item.name || item.short_name || item.text_local_en || item.text; 11 | if (result) { 12 | return result; 13 | } 14 | return ( 15 | item.name ?? item.short_name ?? item.text_local_en ?? item.text ?? item 16 | ); 17 | } 18 | } 19 | 20 | export { itemToString }; 21 | -------------------------------------------------------------------------------- /design/ui/src/libs/dimensions.js: -------------------------------------------------------------------------------- 1 | const explicitDimensionCount = 8; 2 | const explicitDimensions = []; 3 | 4 | for (let i = 0; i < explicitDimensionCount; i++) { 5 | explicitDimensions.push(`dim${i + 1}`); 6 | } 7 | 8 | export { explicitDimensions, explicitDimensionCount }; 9 | -------------------------------------------------------------------------------- /design/ui/src/libs/email-validation.js: -------------------------------------------------------------------------------- 1 | export default function validateEmail(email) { 2 | const re = 3 | /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/; 4 | return re.test(String(email).toLowerCase()); 5 | } 6 | -------------------------------------------------------------------------------- /design/ui/src/libs/group-ids.js: -------------------------------------------------------------------------------- 1 | function parseGroupPart(part) { 2 | if (part.match(/^\d+$/)) { 3 | return Number.parseInt(part); 4 | } else if (part === "None") { 5 | return null; 6 | } 7 | return part; 8 | } 9 | 10 | function splitGroup(group) { 11 | if (group.substr(0, 4) === "grp-") { 12 | return group 13 | .substr(4) 14 | .split(/,/) 15 | .map((item) => parseGroupPart(item)); 16 | } 17 | } 18 | 19 | export { splitGroup }; 20 | -------------------------------------------------------------------------------- /design/ui/src/libs/interest.js: -------------------------------------------------------------------------------- 1 | function createEmptyInterestRecord() { 2 | return {}; 3 | } 4 | 5 | function createLoadingInterestRecord() { 6 | let out = createEmptyInterestRecord(); 7 | out["loading"] = true; 8 | return out; 9 | } 10 | 11 | export { createEmptyInterestRecord, createLoadingInterestRecord }; 12 | -------------------------------------------------------------------------------- /design/ui/src/libs/palettes.js: -------------------------------------------------------------------------------- 1 | const echartPalette = [ 2 | "#5ab1ef", 3 | "#fa6e86", 4 | "#ffb980", 5 | "#19d4ae", 6 | "#0067a6", 7 | "#c4b4e4", 8 | "#d87a80", 9 | "#9cbbff", 10 | "#d9d0c7", 11 | "#87a997", 12 | ]; 13 | 14 | export { echartPalette }; 15 | -------------------------------------------------------------------------------- /design/ui/src/libs/sleep.js: -------------------------------------------------------------------------------- 1 | export default function sleep(ms) { 2 | return new Promise((resolve) => setTimeout(resolve, ms)); 3 | } 4 | -------------------------------------------------------------------------------- /design/ui/src/libs/sources.js: -------------------------------------------------------------------------------- 1 | function badge(item) { 2 | if (item.source && item.source.organization) { 3 | return { 4 | color: "red", 5 | content: "badge.content.organization", 6 | tooltip: "badge.tooltip.organization", 7 | }; 8 | } 9 | if (item.counter_registry_id) { 10 | return { 11 | color: "counterRegistry", 12 | content: "badge.content.registry", 13 | tooltip: "badge.tooltip.registry", 14 | }; 15 | } 16 | return null; 17 | } 18 | 19 | export { badge }; 20 | -------------------------------------------------------------------------------- /design/ui/src/libs/strings.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Generate a pseudo-random string of digits and lower-case letters. 3 | * Math.random().toString(36) produces a string starting with "0.", 4 | * as Math.randow returns a float between 0 * and 1 (excluded). 5 | * Conversion in base 36 produces a string of usually 12 to 13 chars, 6 | * depending on the input. 7 | * @param {number} [length] - Desired length. Max 10-11 characters. 8 | * @returns {string} 9 | */ 10 | export function randomString(length = 10) { 11 | return Math.random() 12 | .toString(36) 13 | .substring(2, length + 2); 14 | } 15 | -------------------------------------------------------------------------------- /design/ui/src/libs/tags.js: -------------------------------------------------------------------------------- 1 | const accessLevels = { 2 | EVERYBODY: 10, 3 | ORG_USERS: 20, 4 | ORG_ADMINS: 30, 5 | CONS_ADMINS: 40, 6 | OWNER: 50, 7 | SYSTEM: 100, 8 | }; 9 | 10 | function tagText(tag) { 11 | if (typeof tag === "string") { 12 | return tag; 13 | } 14 | return `${tag.tag_class.name} / ${tag.name}`; 15 | } 16 | 17 | export { accessLevels, tagText }; 18 | -------------------------------------------------------------------------------- /design/ui/src/libs/unique-mapping.js: -------------------------------------------------------------------------------- 1 | export default function uniqueMapping(mapping) { 2 | // mapping is an object with keys and values 3 | // we want to return a new object with the same keys 4 | // but with unique values 5 | // we accomplish this by adding a number to the end of the value 6 | // if the value is used more than once 7 | let idToCount = new Map(); 8 | let out = {}; 9 | Object.entries(mapping).forEach(([key, value]) => { 10 | let count = idToCount.get(value) || 0; 11 | idToCount.set(value, count + 1); 12 | out[key] = count ? `${value} #${count + 1}` : value; 13 | }); 14 | return out; 15 | } 16 | -------------------------------------------------------------------------------- /design/ui/src/locales/en.json: -------------------------------------------------------------------------------- 1 | { 2 | "message": "hello i18n !!" 3 | } 4 | -------------------------------------------------------------------------------- /design/ui/src/locales/sources.yaml: -------------------------------------------------------------------------------- 1 | en: 2 | badge: 3 | content: 4 | organization: CUSTOM 5 | registry: Registry 6 | tooltip: 7 | organization: Platform was manually added for this organization only 8 | registry: Platform synchronized with the COUNTER registry 9 | 10 | cs: 11 | badge: 12 | content: 13 | organization: VLASTNÍ 14 | registry: Registry 15 | tooltip: 16 | organization: Platform byla ručně přidána pouze pro tuto organizaci 17 | registry: Platforma synchronizovaná s COUNTER registry 18 | -------------------------------------------------------------------------------- /design/ui/src/mixins/formRulesMixin.js: -------------------------------------------------------------------------------- 1 | import validateEmail from "@/libs/email-validation"; 2 | 3 | export default { 4 | data() { 5 | const minPasswordLength = 8; 6 | return { 7 | minPasswordLength, 8 | rules: { 9 | required: (value) => !!value || this.$t("required"), 10 | min: (v) => v.length >= minPasswordLength || this.$t("min_pwd_length"), 11 | email: (v) => !!validateEmail(v) || this.$t("email_required"), 12 | }, 13 | }; 14 | }, 15 | }; 16 | -------------------------------------------------------------------------------- /design/ui/src/pages/FlexiTablePage.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 13 | 14 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /design/ui/src/pages/FlexibleReportsPage.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /design/ui/src/pages/NotFoundPage.vue: -------------------------------------------------------------------------------- 1 | 2 | en: 3 | title: Sorry, but this page does not exist 4 | note: Maybe one of the options from the menu could be helpful. 5 | 6 | cs: 7 | title: Je nám to líto, ale tato stránka neexistuje 8 | note: Možná Vás zaujme některé z položek v nabídce menu. 9 | 10 | 11 | 17 | 18 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /design/ui/src/pages/SushiCredentialsMonthOverviewPage.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /design/ui/src/pages/TagListPage.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 17 | 18 | 30 | -------------------------------------------------------------------------------- /design/ui/src/plugins/vuetify.js: -------------------------------------------------------------------------------- 1 | import Vue from "vue"; 2 | import Vuetify from "vuetify/lib"; 3 | import colors from "vuetify/es5/util/colors"; 4 | 5 | Vue.use(Vuetify); 6 | 7 | export default new Vuetify({ 8 | icons: { 9 | iconfont: "fa", 10 | }, 11 | theme: { 12 | themes: { 13 | light: { 14 | primary: "#2d5854", 15 | secondary: colors.teal.lighten2, 16 | accent: colors.orange.lighten2, 17 | counterRegistry: "#107da6", 18 | anchor: "#35827b", 19 | tertiary: "#666666", 20 | }, 21 | }, 22 | }, 23 | }); 24 | -------------------------------------------------------------------------------- /design/ui/src/store/modules/page-settings.js: -------------------------------------------------------------------------------- 1 | export default { 2 | state: { 3 | pageSettings: {}, 4 | }, 5 | 6 | actions: { 7 | getPageSetting({ state }, { page, key }) { 8 | if (state.pageSettings[page]) { 9 | return state.pageSettings[page][key]; 10 | } 11 | }, 12 | setPageSetting({ commit }, { page, key, value }) { 13 | commit("setPageSetting", { page, key, value }); 14 | }, 15 | }, 16 | 17 | mutations: { 18 | setPageSetting(state, { page, key, value }) { 19 | if (!state.pageSettings[page]) { 20 | state.pageSettings[page] = {}; 21 | } 22 | state.pageSettings[page][key] = value; 23 | }, 24 | }, 25 | }; 26 | -------------------------------------------------------------------------------- /docker/entrypoint-celery-beat.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | celery beat -A config --loglevel=info --pidfile /tmp/celery-beat.pid 4 | -------------------------------------------------------------------------------- /docker/entrypoint-web.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | wait-for-it -p 5432 -h "${POSTGRES_HOST:-celus-postgres}" 6 | wait-for-it -p 6379 -h "${REDIS_HOST:-celus-redis}" 7 | 8 | python manage.py migrate 9 | python manage.py loaddata data/initial-data.json 10 | 11 | uvicorn --host 0.0.0.0 config.wsgi:application --interface wsgi 12 | -------------------------------------------------------------------------------- /docker/nginx.conf: -------------------------------------------------------------------------------- 1 | upstream celus { 2 | server web:8000; 3 | } 4 | 5 | server { 6 | listen 80; 7 | root /var/www/celus/static; 8 | 9 | location / { 10 | try_files $uri $uri/ /index.html; 11 | } 12 | 13 | location /api { 14 | proxy_pass http://celus/api; 15 | } 16 | 17 | location /metrics { 18 | proxy_pass http://celus/metrics; 19 | } 20 | 21 | location /wsEc67YNV2sq { 22 | proxy_pass http://celus/wsEc67YNV2sq; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /docs/Uživatelská dokumentace.odt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techlib/celus/5ad1bd73a210cb5456eeea442913fa4f7649b7b5/docs/Uživatelská dokumentace.odt -------------------------------------------------------------------------------- /docs/advanced-topics.rst: -------------------------------------------------------------------------------- 1 | =============== 2 | Advanced topics 3 | =============== 4 | 5 | This part describes more involved and technically demanding features of Celus. 6 | 7 | .. toctree:: 8 | :maxdepth: 3 9 | :caption: Contents: 10 | 11 | external-api.rst 12 | -------------------------------------------------------------------------------- /docs/ansible/celus.yaml: -------------------------------------------------------------------------------- 1 | 2 | - hosts: all 3 | roles: 4 | - {role: celus, tags: ['celus']} 5 | -------------------------------------------------------------------------------- /docs/ansible/roles/celus/defaults/main.yml: -------------------------------------------------------------------------------- 1 | # base of git repo path - when project_name is added this path, it should form 2 | # complete path to the project git repo 3 | # should end with / 4 | project_git_path_base: https://github.com/techlib/ 5 | 6 | # default branch to check when getting project data from git 7 | project_branch: master 8 | 9 | # default settings version to use 10 | settings_version: production 11 | 12 | settings_dir: config 13 | 14 | # the name of the PostgreSQL database user to make owner of the database 15 | # should match django settings 16 | db_user: "{{ db_name }}" 17 | 18 | 19 | postgres_hba_file: "/var/lib/pgsql/data/pg_hba.conf" 20 | -------------------------------------------------------------------------------- /docs/ansible/roles/celus/handlers/main.yaml: -------------------------------------------------------------------------------- 1 | - name: restart apache 2 | service: name=httpd state=reloaded 3 | 4 | - name: reload systemd 5 | systemd: daemon_reload=yes name=celery 6 | 7 | - name: restart celery 8 | service: name=celery state=restarted 9 | 10 | - name: restart celerybeat 11 | service: name=celerybeat state=restarted 12 | 13 | - name: reload postgresql 14 | service: name=postgresql state=reloaded 15 | -------------------------------------------------------------------------------- /docs/ansible/roles/celus/templates/etc_confd_celerybeat.j2: -------------------------------------------------------------------------------- 1 | DJANGO_SETTINGS_MODULE={{ settings_dir }}.settings.{{ settings_version }} 2 | 3 | # Absolute or relative path to the 'celery' command: 4 | CELERY_BIN="/opt/virtualenvs/{{ project_name }}/bin/celery" 5 | 6 | # App instance to use 7 | # comment out this line if you don't use an app 8 | CELERY_APP="{{ settings_dir }}" 9 | 10 | # Extra arguments to celerybeat 11 | # disabled on purpose - we do not use django_celery_beat anymore 12 | #CELERYBEAT_OPTS="--scheduler django_celery_beat.schedulers:DatabaseScheduler" 13 | 14 | CELERYD_LOG_LEVEL="info" 15 | 16 | CELERYD_LOG_FILE="/var/log/celery/beat.log" 17 | CELERYD_PID_FILE="/var/run/celery/beat.pid" 18 | -------------------------------------------------------------------------------- /docs/ansible/roles/celus/templates/etc_cron_daily_backup_db.j2: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | /bin/su postgres -c "/usr/bin/pg_dump {{ db_name }}" | /usr/bin/zstd -c9 > /root/backup/{{ db_name }}-dump-`date "+%Y%m%d"`.sql.zst 4 | /usr/bin/tar c -I '/usr/bin/zstd -9' -f /root/backup/media-`date "+%Y%m%d"`.tar.zst /var/www/{{ project_name }}/media/ 5 | 6 | # delete old backups 7 | /usr/bin/find /root/backup/ -mtime +10 -exec rm {} \; 8 | -------------------------------------------------------------------------------- /docs/ansible/roles/celus/templates/etc_cron_daily_cleanup_django_sessions.j2: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | DJANGO_SETTINGS_MODULE={{ settings_dir }}.settings.{{ settings_version }} /opt/virtualenvs/{{ project_name }}/bin/python /opt/{{ project_name }}/manage.py clearsessions 4 | -------------------------------------------------------------------------------- /docs/ansible/roles/celus/templates/etc_logrotated_celery.j2: -------------------------------------------------------------------------------- 1 | /var/log/celery/*.log { 2 | weekly 3 | missingok 4 | rotate 520 5 | compress 6 | compresscmd /usr/bin/xz 7 | compressext .xz 8 | uncompresscmd /usr/bin/unxz 9 | delaycompress 10 | notifempty 11 | create 0640 celery celery 12 | dateext 13 | dateformat -%Y-%m-%d 14 | } 15 | -------------------------------------------------------------------------------- /docs/ansible/roles/celus/templates/etc_profiled_virtualenvwrapper.sh.j2: -------------------------------------------------------------------------------- 1 | export WORKON_HOME=/opt/virtualenvs/ 2 | -------------------------------------------------------------------------------- /docs/ansible/roles/celus/templates/root_activate_virtualenv.sh.j2: -------------------------------------------------------------------------------- 1 | source /etc/profile.d/virtualenvwrapper.sh 2 | source /usr/local/bin/virtualenvwrapper.sh 3 | export DJANGO_SETTINGS_MODULE={{ settings_dir }}.settings.{{ settings_version }} 4 | workon {{ project_name }} 5 | -------------------------------------------------------------------------------- /docs/ansible/roles/celus/templates/secret_settings.json.j2: -------------------------------------------------------------------------------- 1 | { 2 | "SECRET_KEY": "{{ django_secret_key }}", 3 | "DB_PASSWORD": "{{ db_password }}", 4 | "ERMS_API_URL": "{{ erms_url }}" 5 | } 6 | -------------------------------------------------------------------------------- /docs/examples/ex-just-values.csv: -------------------------------------------------------------------------------- 1 | "2019-01","2019-02","2019-03" 2 | 10,20,30 3 | -------------------------------------------------------------------------------- /docs/examples/ex-just-values.ods: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techlib/celus/5ad1bd73a210cb5456eeea442913fa4f7649b7b5/docs/examples/ex-just-values.ods -------------------------------------------------------------------------------- /docs/examples/ex-metric.csv: -------------------------------------------------------------------------------- 1 | "Metric","Jan-2018","Feb-2018","Mar-2018","Apr-2018","May-2018","Jun-2018" 2 | "Exports",1206,24094,24094,24094,24094,24094 3 | -------------------------------------------------------------------------------- /docs/examples/ex-metric.ods: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techlib/celus/5ad1bd73a210cb5456eeea442913fa4f7649b7b5/docs/examples/ex-metric.ods -------------------------------------------------------------------------------- /docs/examples/ex-metric.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techlib/celus/5ad1bd73a210cb5456eeea442913fa4f7649b7b5/docs/examples/ex-metric.png -------------------------------------------------------------------------------- /docs/examples/ex-title-metric-publisher-success.ods: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techlib/celus/5ad1bd73a210cb5456eeea442913fa4f7649b7b5/docs/examples/ex-title-metric-publisher-success.ods -------------------------------------------------------------------------------- /docs/examples/ex-title-metric-publisher-success.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techlib/celus/5ad1bd73a210cb5456eeea442913fa4f7649b7b5/docs/examples/ex-title-metric-publisher-success.png -------------------------------------------------------------------------------- /docs/examples/ex-title-metric-publisher.ods: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techlib/celus/5ad1bd73a210cb5456eeea442913fa4f7649b7b5/docs/examples/ex-title-metric-publisher.ods -------------------------------------------------------------------------------- /docs/examples/ex-title.ods: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techlib/celus/5ad1bd73a210cb5456eeea442913fa4f7649b7b5/docs/examples/ex-title.ods -------------------------------------------------------------------------------- /docs/examples/ex-title.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techlib/celus/5ad1bd73a210cb5456eeea442913fa4f7649b7b5/docs/examples/ex-title.png -------------------------------------------------------------------------------- /docs/images/api-key-create.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techlib/celus/5ad1bd73a210cb5456eeea442913fa4f7649b7b5/docs/images/api-key-create.png -------------------------------------------------------------------------------- /docs/images/api-key-get-value.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techlib/celus/5ad1bd73a210cb5456eeea442913fa4f7649b7b5/docs/images/api-key-get-value.png -------------------------------------------------------------------------------- /docs/images/dashboard.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techlib/celus/5ad1bd73a210cb5456eeea442913fa4f7649b7b5/docs/images/dashboard.png -------------------------------------------------------------------------------- /docs/images/dja_add_chart_definition.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techlib/celus/5ad1bd73a210cb5456eeea442913fa4f7649b7b5/docs/images/dja_add_chart_definition.png -------------------------------------------------------------------------------- /docs/images/dja_add_dimension.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techlib/celus/5ad1bd73a210cb5456eeea442913fa4f7649b7b5/docs/images/dja_add_dimension.png -------------------------------------------------------------------------------- /docs/images/dja_add_report_data_view.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techlib/celus/5ad1bd73a210cb5456eeea442913fa4f7649b7b5/docs/images/dja_add_report_data_view.png -------------------------------------------------------------------------------- /docs/images/dja_add_report_type.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techlib/celus/5ad1bd73a210cb5456eeea442913fa4f7649b7b5/docs/images/dja_add_report_type.png -------------------------------------------------------------------------------- /docs/images/dja_add_report_type_filled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techlib/celus/5ad1bd73a210cb5456eeea442913fa4f7649b7b5/docs/images/dja_add_report_type_filled.png -------------------------------------------------------------------------------- /docs/images/dja_add_report_view_to_chart_type.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techlib/celus/5ad1bd73a210cb5456eeea442913fa4f7649b7b5/docs/images/dja_add_report_view_to_chart_type.png -------------------------------------------------------------------------------- /docs/images/dja_report_data_view_tr_jr1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techlib/celus/5ad1bd73a210cb5456eeea442913fa4f7649b7b5/docs/images/dja_report_data_view_tr_jr1.png -------------------------------------------------------------------------------- /docs/images/dja_sushi_app.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techlib/celus/5ad1bd73a210cb5456eeea442913fa4f7649b7b5/docs/images/dja_sushi_app.png -------------------------------------------------------------------------------- /docs/images/dja_sushi_attempt_delete.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techlib/celus/5ad1bd73a210cb5456eeea442913fa4f7649b7b5/docs/images/dja_sushi_attempt_delete.png -------------------------------------------------------------------------------- /docs/images/sidebar_sushi_management.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techlib/celus/5ad1bd73a210cb5456eeea442913fa4f7649b7b5/docs/images/sidebar_sushi_management.png -------------------------------------------------------------------------------- /docs/images/sushi_attempt_list.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techlib/celus/5ad1bd73a210cb5456eeea442913fa4f7649b7b5/docs/images/sushi_attempt_list.png -------------------------------------------------------------------------------- /docs/images/sushi_button_close.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techlib/celus/5ad1bd73a210cb5456eeea442913fa4f7649b7b5/docs/images/sushi_button_close.png -------------------------------------------------------------------------------- /docs/images/sushi_button_delete.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techlib/celus/5ad1bd73a210cb5456eeea442913fa4f7649b7b5/docs/images/sushi_button_delete.png -------------------------------------------------------------------------------- /docs/images/sushi_button_save.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techlib/celus/5ad1bd73a210cb5456eeea442913fa4f7649b7b5/docs/images/sushi_button_save.png -------------------------------------------------------------------------------- /docs/images/sushi_button_save_and_test.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techlib/celus/5ad1bd73a210cb5456eeea442913fa4f7649b7b5/docs/images/sushi_button_save_and_test.png -------------------------------------------------------------------------------- /docs/images/sushi_create_dialog.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techlib/celus/5ad1bd73a210cb5456eeea442913fa4f7649b7b5/docs/images/sushi_create_dialog.png -------------------------------------------------------------------------------- /docs/images/sushi_credentials_table.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techlib/celus/5ad1bd73a210cb5456eeea442913fa4f7649b7b5/docs/images/sushi_credentials_table.png -------------------------------------------------------------------------------- /docs/images/sushi_edit_dialog.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techlib/celus/5ad1bd73a210cb5456eeea442913fa4f7649b7b5/docs/images/sushi_edit_dialog.png -------------------------------------------------------------------------------- /docs/images/sushi_test_dialog_start.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techlib/celus/5ad1bd73a210cb5456eeea442913fa4f7649b7b5/docs/images/sushi_test_dialog_start.png -------------------------------------------------------------------------------- /docs/images/sushi_test_failure.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techlib/celus/5ad1bd73a210cb5456eeea442913fa4f7649b7b5/docs/images/sushi_test_failure.png -------------------------------------------------------------------------------- /docs/images/sushi_test_running.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techlib/celus/5ad1bd73a210cb5456eeea442913fa4f7649b7b5/docs/images/sushi_test_running.png -------------------------------------------------------------------------------- /docs/images/sushi_test_running_cs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techlib/celus/5ad1bd73a210cb5456eeea442913fa4f7649b7b5/docs/images/sushi_test_running_cs.png -------------------------------------------------------------------------------- /docs/images/sushi_test_success.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techlib/celus/5ad1bd73a210cb5456eeea442913fa4f7649b7b5/docs/images/sushi_test_success.png -------------------------------------------------------------------------------- /docs/images/sushi_top_bar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techlib/celus/5ad1bd73a210cb5456eeea442913fa4f7649b7b5/docs/images/sushi_top_bar.png -------------------------------------------------------------------------------- /docs/images/tag-class-creation-5_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techlib/celus/5ad1bd73a210cb5456eeea442913fa4f7649b7b5/docs/images/tag-class-creation-5_0.png -------------------------------------------------------------------------------- /docs/images/tag-creation-5_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techlib/celus/5ad1bd73a210cb5456eeea442913fa4f7649b7b5/docs/images/tag-creation-5_0.png -------------------------------------------------------------------------------- /docs/images/tag-list-new-tag-5_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techlib/celus/5ad1bd73a210cb5456eeea442913fa4f7649b7b5/docs/images/tag-list-new-tag-5_0.png -------------------------------------------------------------------------------- /docs/images/tag-title-assign-2-5_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techlib/celus/5ad1bd73a210cb5456eeea442913fa4f7649b7b5/docs/images/tag-title-assign-2-5_0.png -------------------------------------------------------------------------------- /docs/images/tag-title-assign-5_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techlib/celus/5ad1bd73a210cb5456eeea442913fa4f7649b7b5/docs/images/tag-title-assign-5_0.png -------------------------------------------------------------------------------- /docs/images/tagging-batch-assign-5_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techlib/celus/5ad1bd73a210cb5456eeea442913fa4f7649b7b5/docs/images/tagging-batch-assign-5_0.png -------------------------------------------------------------------------------- /docs/images/tagging-batch-done-5_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techlib/celus/5ad1bd73a210cb5456eeea442913fa4f7649b7b5/docs/images/tagging-batch-done-5_0.png -------------------------------------------------------------------------------- /docs/images/tagging-batch-example-5_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techlib/celus/5ad1bd73a210cb5456eeea442913fa4f7649b7b5/docs/images/tagging-batch-example-5_0.png -------------------------------------------------------------------------------- /docs/images/tagging-batch-list-2-5_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techlib/celus/5ad1bd73a210cb5456eeea442913fa4f7649b7b5/docs/images/tagging-batch-list-2-5_0.png -------------------------------------------------------------------------------- /docs/images/tagging-batch-list-3-5_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techlib/celus/5ad1bd73a210cb5456eeea442913fa4f7649b7b5/docs/images/tagging-batch-list-3-5_0.png -------------------------------------------------------------------------------- /docs/images/tagging-batch-list-5_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techlib/celus/5ad1bd73a210cb5456eeea442913fa4f7649b7b5/docs/images/tagging-batch-list-5_0.png -------------------------------------------------------------------------------- /docs/images/tagging-batch-preprocess-5_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techlib/celus/5ad1bd73a210cb5456eeea442913fa4f7649b7b5/docs/images/tagging-batch-preprocess-5_0.png -------------------------------------------------------------------------------- /docs/images/tagging-batch-upload-5_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techlib/celus/5ad1bd73a210cb5456eeea442913fa4f7649b7b5/docs/images/tagging-batch-upload-5_0.png -------------------------------------------------------------------------------- /docs/images/tags-management-empty-5_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techlib/celus/5ad1bd73a210cb5456eeea442913fa4f7649b7b5/docs/images/tags-management-empty-5_0.png -------------------------------------------------------------------------------- /docs/images/tags-reporting-filter-5_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techlib/celus/5ad1bd73a210cb5456eeea442913fa4f7649b7b5/docs/images/tags-reporting-filter-5_0.png -------------------------------------------------------------------------------- /docs/images/tags-reporting-grouping-1-5_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techlib/celus/5ad1bd73a210cb5456eeea442913fa4f7649b7b5/docs/images/tags-reporting-grouping-1-5_0.png -------------------------------------------------------------------------------- /docs/images/tags-reporting-grouping-remainder-5_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techlib/celus/5ad1bd73a210cb5456eeea442913fa4f7649b7b5/docs/images/tags-reporting-grouping-remainder-5_0.png -------------------------------------------------------------------------------- /docs/images/tags-title-filtering-1-5_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techlib/celus/5ad1bd73a210cb5456eeea442913fa4f7649b7b5/docs/images/tags-title-filtering-1-5_0.png -------------------------------------------------------------------------------- /docs/images/tags-title-filtering-2-5_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techlib/celus/5ad1bd73a210cb5456eeea442913fa4f7649b7b5/docs/images/tags-title-filtering-2-5_0.png -------------------------------------------------------------------------------- /docs/images/title-tag-filtering-5_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techlib/celus/5ad1bd73a210cb5456eeea442913fa4f7649b7b5/docs/images/title-tag-filtering-5_0.png -------------------------------------------------------------------------------- /docs/requirements.txt: -------------------------------------------------------------------------------- 1 | sphinx==2.3.1 2 | -------------------------------------------------------------------------------- /docs/topics.rst: -------------------------------------------------------------------------------- 1 | ===================== 2 | Topical documentation 3 | ===================== 4 | 5 | The following documents cover specific topics: 6 | 7 | .. toctree:: 8 | :maxdepth: 3 9 | :caption: Contents: 10 | 11 | tags.rst 12 | -------------------------------------------------------------------------------- /dump_basic_defs.sh: -------------------------------------------------------------------------------- 1 | python manage.py dumpdata --indent=2 sushi.counterreporttype logs.reporttype logs.metric logs.dimension logs.reporttypetodimension logs.dimensiontext 2 | -------------------------------------------------------------------------------- /k8s/generate.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | kompose -f ../docker/docker-compose-k8s.yml convert 4 | -------------------------------------------------------------------------------- /k8s/ingress-config.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ConfigMap 3 | metadata: 4 | name: ingress-nginx-controller 5 | namespace: ingress-nginx 6 | data: 7 | use-proxy-protocol: "true" 8 | use-forwarded-headers: "true" 9 | -------------------------------------------------------------------------------- /k8s/letsencrypt-issuer.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: cert-manager.io/v1alpha2 2 | kind: ClusterIssuer 3 | metadata: 4 | name: letsencrypt 5 | namespace: cert-manager 6 | spec: 7 | acme: 8 | # The ACME server URL 9 | server: https://acme-v02.api.letsencrypt.org/directory 10 | # Email address used for ACME registration 11 | email: root@celus.net 12 | # Name of a secret used to store the ACME account private key 13 | privateKeySecretRef: 14 | name: letsencrypt 15 | # Enable the HTTP-01 challenge provider 16 | solvers: 17 | - http01: 18 | ingress: 19 | class: nginx 20 | -------------------------------------------------------------------------------- /k8s/nginx-service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | annotations: 5 | kompose.cmd: kompose -f ../docker/docker-compose-k8s.yml convert 6 | kompose.version: 1.21.0 (HEAD) 7 | creationTimestamp: null 8 | labels: 9 | io.kompose.service: nginx 10 | name: celus-nginx 11 | spec: 12 | ports: 13 | - name: "80" 14 | port: 80 15 | targetPort: 80 16 | selector: 17 | io.kompose.service: nginx 18 | status: 19 | loadBalancer: {} 20 | -------------------------------------------------------------------------------- /k8s/postgres-service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | annotations: 5 | kompose.cmd: kompose -f ../docker/docker-compose-k8s.yml convert 6 | kompose.version: 1.21.0 (HEAD) 7 | creationTimestamp: null 8 | labels: 9 | io.kompose.service: postgres 10 | name: celus-postgres 11 | spec: 12 | ports: 13 | - name: "5432" 14 | port: 5432 15 | targetPort: 5432 16 | selector: 17 | io.kompose.service: postgres 18 | status: 19 | loadBalancer: {} 20 | -------------------------------------------------------------------------------- /k8s/redis-service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | annotations: 5 | kompose.cmd: kompose -f ../docker/docker-compose-k8s.yml convert 6 | kompose.version: 1.21.0 (HEAD) 7 | creationTimestamp: null 8 | labels: 9 | io.kompose.service: redis 10 | name: celus-redis 11 | spec: 12 | ports: 13 | - name: "6379" 14 | port: 6379 15 | targetPort: 6379 16 | selector: 17 | io.kompose.service: redis 18 | status: 19 | loadBalancer: {} 20 | -------------------------------------------------------------------------------- /k8s/secrets/secrets-celus.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Secret 3 | metadata: 4 | name: celus 5 | type: Opaque 6 | stringData: 7 | DJANGO_SECRET_KEY: 8 | ERMS_API_URL: 9 | POSTGRES_DB: 10 | POSTGRES_USER: 11 | POSTGRES_PASSWORD: 12 | POSTGRES_HOST: 13 | POSTGRES_PORT: 14 | REDIS_HOST: 15 | REDIS_LOCATION: 16 | CELERY_BROKER_URL: 17 | -------------------------------------------------------------------------------- /k8s/secrets/secrets-gitlab.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Secret 3 | metadata: 4 | name: gitlab 5 | type: kubernetes.io/dockerconfigjson 6 | data: 7 | .dockerconfigjson: 8 | -------------------------------------------------------------------------------- /k8s/web-service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | annotations: 5 | kompose.cmd: kompose -f ../docker/docker-compose-k8s.yml convert 6 | kompose.version: 1.21.0 (HEAD) 7 | creationTimestamp: null 8 | labels: 9 | io.kompose.service: web 10 | name: celus-web 11 | spec: 12 | ports: 13 | - name: "8000" 14 | port: 8000 15 | targetPort: 8000 16 | selector: 17 | io.kompose.service: web 18 | status: 19 | loadBalancer: {} 20 | -------------------------------------------------------------------------------- /pytest.ini: -------------------------------------------------------------------------------- 1 | [pytest] 2 | DJANGO_SETTINGS_MODULE = config.settings.test 3 | python_files = tests.py test_*.py 4 | addopts = --reuse-db --random-order-bucket=global 5 | norecursedirs = design doc pycounter media 6 | markers = 7 | now: marker for tests which are currently developed 8 | clickhouse: uses clickhouse database, automatically create and destroy clickhouse db, enforce single thread 9 | env = 10 | CELUS_ADMIN_SITE_PATH=custom-admin/ 11 | CLICKHOUSE_DB=celus_test 12 | -------------------------------------------------------------------------------- /readthedocs.yaml: -------------------------------------------------------------------------------- 1 | # .readthedocs.yml 2 | # Read the Docs configuration file 3 | # See https://docs.readthedocs.io/en/stable/config-file/v2.html for details 4 | 5 | # Required 6 | version: 2 7 | 8 | # Build documentation in the docs/ directory with Sphinx 9 | sphinx: 10 | configuration: docs/conf.py 11 | 12 | # Optionally build your docs in additional formats such as PDF and ePub 13 | formats: all 14 | 15 | # Optionally set the version of Python and requirements required to build your docs 16 | python: 17 | install: 18 | - requirements: docs/requirements.txt 19 | -------------------------------------------------------------------------------- /start_celery.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | export PYTHONBREAKPOINT=celery.contrib.rdb.set_trace 3 | watchmedo auto-restart -d apps/ -d config/ -p '*.py' -R -- celery -A config worker -Q export,celery,interest,sushi,import,normal,preflight -l DEBUG 4 | -------------------------------------------------------------------------------- /start_celerybeat.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | watchmedo auto-restart -d apps/ -d config/ -p '*.py' -R -- celery -A config beat -l DEBUG 3 | -------------------------------------------------------------------------------- /test-data/counter4/4_PR1_invalid_requestor.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 2000 5 | Error 6 | Unknown or invalid requestor ID 7 | 8 | 9 | -------------------------------------------------------------------------------- /test-data/counter4/counter4_jr1_empty.tsv: -------------------------------------------------------------------------------- 1 | Journal Report 1 (R4) Number of Successful Full-Text Article Requests by Month and Journal 2 | Title 3 | 1111111111 4 | Period covered by Report: 5 | 2020-01-01 to 2020-01-31 6 | Date run: 7 | 2020-07-28 8 | Journal Publisher Platform Journal DOI Proprietary Identifier Print ISSN Online ISSN Reporting Period Total Reporting Period HTML Reporting Period PDF Jan-2020 9 | -------------------------------------------------------------------------------- /test-data/counter5/5_DR_ProQuestEbookCentral_exception.json: -------------------------------------------------------------------------------- 1 | { 2 | "body": null, 3 | "response": { 4 | "number": 1001, 5 | "severity": "Error", 6 | "message": "executeSushiAnalysis" 7 | } 8 | } -------------------------------------------------------------------------------- /test-data/counter5/TR-wrong-encoding.csv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techlib/celus/5ad1bd73a210cb5456eeea442913fa4f7649b7b5/test-data/counter5/TR-wrong-encoding.csv -------------------------------------------------------------------------------- /test-data/counter5/counter5_table_pr_empty.csv: -------------------------------------------------------------------------------- 1 | Report_Name,Platform Master Report,,,,,,,,, 2 | Report_ID,PR,,,,,,,,, 3 | Release,5,,,,,,,,, 4 | Institution_Name,Sample University,,,,,,,,, 5 | Institution_ID,isni=1234567890,,,,,,,,, 6 | Metric_Types,as selected,,,,,,,,, 7 | Report_Filters,as selected,,,,,,,,, 8 | Report_Attributes,as selected,,,,,,,,, 9 | Exceptions,,,,,,,,,, 10 | Reporting_Period,2017-01-01 to 2017-06-30,,,,,,,,, 11 | Created,2017-05-25,,,,,,,,, 12 | Created_By,Publisher Platform Alpha,,,,,,,,, 13 | ,,,,,,,,,, 14 | Platform,Data_Type,Access_Method,Metric_Type,Reporting_Period_Total,Jan-2017,Feb-2017,Mar-2017,Apr-2017,May-2017,Jun-2017 15 | -------------------------------------------------------------------------------- /test-data/counter5/data_incorrect.json: -------------------------------------------------------------------------------- 1 | { 2 | "foo": "bar" 3 | } 4 | -------------------------------------------------------------------------------- /test-data/counter5/error-in-root.json: -------------------------------------------------------------------------------- 1 | {"Report_Header":{"Requestor_ID":"user@example.com","Report_ID":"tr","Customer_ID":"XXXXXXXXXXXXXX","Institution_Name":"National Library","Created_By":"Celus LLC.","Report_Filters":[{"Value":"2019-12","Name":"end_date"},{"Value":"2019-01","Name":"begin_date"},{"Value":"YOP|Access_Method|Access_Type|Data_Type|Section_Type","Name":"attributes_to_show"}]},"Exception":{"Message":"Got response code: 404 for request: https://example.com/path/path","Severity":"Error","Code":2090},"Release":5,"Created":"2020-07-03 02:02:30-0700"} 2 | -------------------------------------------------------------------------------- /test-data/counter5/invalid-customer.json: -------------------------------------------------------------------------------- 1 | {"Report_Header":{"Requestor_ID":"my.uni","Report_ID":"dr","Customer_ID":"000000000","Created_By":"Celus LLC.","Report_Filters":[{"Value":"2021-12","Name":"end_date"},{"Value":"2021-12","Name":"begin_date"},{"Value":"Access_Method|Data_Type","Name":"attributes_to_show"}]},"Exception":{"Message":"Invalid Customer Id","Severity":"Fatal","Code":1030},"Release":5,"Created":"2022-01-01 00:00:00-0000"} 2 | -------------------------------------------------------------------------------- /test-data/counter5/naked_error.json: -------------------------------------------------------------------------------- 1 | { 2 | "Code": 1011, 3 | "Severity": "Warning", 4 | "Message": "Report Queued for Processing", 5 | "Help_URL": "https://support.jstor.org", 6 | "Data": "Report is currently queued for processing. Please retry the request after some reasonable time." 7 | } 8 | -------------------------------------------------------------------------------- /test-data/counter5/naked_error_3000.json: -------------------------------------------------------------------------------- 1 | { 2 | "Code":3000, 3 | "Severity":"Error", 4 | "Message":"Report Not Supported", 5 | "Data":"Unknown report requested: [REPORT-ID]" 6 | } 7 | -------------------------------------------------------------------------------- /test-data/counter5/naked_error_lowercase.json: -------------------------------------------------------------------------------- 1 | {"number":1001,"severity":"Error","message":"executeSushiAnalysis"} 2 | -------------------------------------------------------------------------------- /test-data/counter5/naked_errors.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "Code": 1011, 4 | "Severity": "Warning", 5 | "Message": "Report Queued for Processing", 6 | "Help_URL": "https://support.jstor.org", 7 | "Data": "Report is currently queued for processing. Please retry the request after some reasonable time." 8 | }, 9 | { 10 | "Code": 3060, 11 | "Severity": "Warning", 12 | "Message": "Invalid Report Filter Value", 13 | "Help_URL": "https://support.jstor.org", 14 | "Data": "platform not able to be changed from its value of jstor" 15 | } 16 | ] 17 | -------------------------------------------------------------------------------- /test-data/counter5/no_data.json: -------------------------------------------------------------------------------- 1 | {"Report_Header":{"Customer_ID":"ffffffff-ffff-ffff-ffff-ffffffffffff","Institution_Name":"My Institution","Institution_ID":[{"Type":"Proprietary","Value":"lyb:DDDDDDDDDDDDDD"}],"Report_Filters":[{"Name":"End_Date","Value":"2018-11-30"},{"Name":"Begin_Date","Value":"2018-11-01"}],"Created":"2020-07-27T07:47:13Z","Created_By":"Celus LLC.","Release":"5","Report_Name":"Title Master Report","Report_ID":"TR"},"Report_Items":[]} 2 | -------------------------------------------------------------------------------- /test-data/counter5/no_json.txt: -------------------------------------------------------------------------------- 1 | HTTP Error 400: Bad request 2 | -------------------------------------------------------------------------------- /test-data/custom/custom_data-2d-3x2x3-endate.csv: -------------------------------------------------------------------------------- 1 | "Source","Metric","Jan 2019","Feb 2019","Mar 2019" 2 | "Title 1","Metric 1",1,4,7 3 | "Title 1","Metric 2",10,20,30 4 | "Title 2","Metric 1",2,5,8 5 | "Title 2","Metric 2",20,30,40 6 | "Title 3","Metric 1",3,6,9 7 | "Title 3","Metric 2",30,40,50 8 | -------------------------------------------------------------------------------- /test-data/custom/custom_data-2d-3x2x3-isodate.csv: -------------------------------------------------------------------------------- 1 | "Source","Metric","2019-01","2019-02","2019-03" 2 | "Title 1","Metric 1",1,4,7 3 | "Title 1","Metric 2",10,20,30 4 | "Title 2","Metric 1",2,5,8 5 | "Title 2","Metric 2",20,30,40 6 | "Title 3","Metric 1",3,6,9 7 | "Title 3","Metric 2",30,40,50 8 | -------------------------------------------------------------------------------- /test-data/custom/custom_data-2d-3x2x3-org-isodate-single.csv: -------------------------------------------------------------------------------- 1 | "Title","Metric","Organization","2019-01","2019-02","2019-03" 2 | "Title 1","Metric 1","Org1",1,4,7 3 | "Title 1","Metric 2","Org1",10,20,30 4 | "Title 2","Metric 1","Org1",2,5,8 5 | "Title 2","Metric 2","Org1",20,30,40 6 | "Title 3","Metric 1","Org1",3,6,9 7 | "Title 3","Metric 2","Org1",30,40,50 8 | -------------------------------------------------------------------------------- /test-data/custom/custom_data-2d-3x2x3-org-isodate.csv: -------------------------------------------------------------------------------- 1 | "Title","Metric","Organization","2019-01","2019-02","2019-03" 2 | "Title 1","Metric 1","Org1",1,4,7 3 | "Title 1","Metric 2","Org1",10,20,30 4 | "Title 2","Metric 1","Org1",2,5,8 5 | "Title 2","Metric 2","Org1",20,30,40 6 | "Title 3","Metric 1","Org1",3,6,9 7 | "Title 3","Metric 2","Org1",30,40,50 8 | "Title 1","Metric 1","Org2",3,7,10 9 | "Title 1","Metric 2","Org2",11,21,31 10 | "Title 2","Metric 1","Org2",4,7,11 11 | "Title 2","Metric 2","Org2",21,31,41 12 | "Title 3","Metric 1","Org2",5,9,12 13 | "Title 3","Metric 2","Org2",31,41,51 14 | -------------------------------------------------------------------------------- /test-data/custom/custom_data-nibbler-simple.csv: -------------------------------------------------------------------------------- 1 | "Title","2020-01" 2 | "T1","10" 3 | "T2","11" 4 | "T3","12" 5 | "T4","13" 6 | "T5","14" 7 | -------------------------------------------------------------------------------- /test-data/custom/custom_data-simple-3x3-endate.csv: -------------------------------------------------------------------------------- 1 | "Source","Jan 2019","Feb 2019","Mar 2019" 2 | "Title 1",1,4,7 3 | "Title 2",2,5,8 4 | "Title 3",3,6,9 5 | -------------------------------------------------------------------------------- /test-data/custom/custom_data-simple-3x3-isodate.csv: -------------------------------------------------------------------------------- 1 | "Source","2019-01","2019-02","2019-03" 2 | "Title 1",1,4,7 3 | "Title 2",2,5,8 4 | "Title 3",3,6,9 5 | -------------------------------------------------------------------------------- /test-data/releases/test_empty_releases.yaml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techlib/celus/5ad1bd73a210cb5456eeea442913fa4f7649b7b5/test-data/releases/test_empty_releases.yaml -------------------------------------------------------------------------------- /test-data/tagging_batch/plain-title-list-with-tags.csv: -------------------------------------------------------------------------------- 1 | "Name","ISBN","issn","eISSN","note","tag" 2 | "Foo",9780787960186,,,"isbn13","prase" 3 | "Bar",,"1234-5678",,"issn","hroch" 4 | "Baz",,"9874-6314","2546-5794","both issn and eissn","prase" 5 | "Boo",,"2546-5794",,"eissn already seen","prase" 6 | "Moo",1614294410,,,"isnb10","prase" 7 | "Mar",,,"1234-5678","eissn matching previously seen issn","praze" 8 | "Maz",9781614295365,"2345-6789",,"isbn13 + issn", 9 | -------------------------------------------------------------------------------- /test-data/tagging_batch/plain-title-list.csv: -------------------------------------------------------------------------------- 1 | Name,ISBN,issn,eISSN,note 2 | Foo,9780787960186,,,isbn13 3 | Bar,,1234-5678,,issn 4 | Baz,,9874-6314,2546-5794,both issn and eissn 5 | Moo,1614294410,,,isnb10 6 | Mar,,,1234-5678,eissn matching previously seen issn 7 | Maz,9781614295365,2345-6789,,isbn13 + issn 8 | -------------------------------------------------------------------------------- /test-data/tagging_batch/scopus-topics-test.csv: -------------------------------------------------------------------------------- 1 | ISSN,eISSN,Codes 2 | 1234-5678,,1000 3 | 2345-6789,,1101; 1106 4 | ,9999-999X,1107 5 | -------------------------------------------------------------------------------- /test-data/tagging_batch/simple-title-list-with-bom.csv: -------------------------------------------------------------------------------- 1 | ISSN 2 | 0001-9909 3 | 0306-1078 4 | 0013-7227 5 | 0013-8215 6 | 0014-0856 7 | 0022-2097 8 | 0146-8693 9 | 0002-7189 10 | 0007-5027 11 | 0195-6167 12 | 0027-4631 13 | 0036-9543 14 | 0037-3222 15 | 0084-4144 16 | -------------------------------------------------------------------------------- /test_coverage.sh: -------------------------------------------------------------------------------- 1 | DJANGO_SETTINGS_MODULE=config.settings.devel pytest --cov=publications --cov=core --cov=logs --cov=erms --cov=organizations --cov=sushi 2 | -------------------------------------------------------------------------------- /test_scenarios/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techlib/celus/5ad1bd73a210cb5456eeea442913fa4f7649b7b5/test_scenarios/__init__.py --------------------------------------------------------------------------------