fairdm 0.3.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- demo/README.md +466 -0
- demo/__init__.py +1 -0
- demo/admin.py +519 -0
- demo/apps.py +22 -0
- demo/config.py +460 -0
- demo/factories.py +374 -0
- demo/filters.py +475 -0
- demo/forms.py +584 -0
- demo/management/__init__.py +0 -0
- demo/management/commands/__init__.py +0 -0
- demo/management/commands/seed_contributors.py +42 -0
- demo/management/commands/seed_overviews.py +39 -0
- demo/management/commands/seed_profiles.py +503 -0
- demo/management/commands/seed_record_search.py +40 -0
- demo/migrations/0001_initial.py +724 -0
- demo/migrations/0002_alter_customparentsample_options.py +17 -0
- demo/migrations/0003_icp_ms_measurement_uncertainty_and_more.py +24 -0
- demo/migrations/__init__.py +0 -0
- demo/models.py +690 -0
- demo/options.py +1 -0
- demo/plugins.py +186 -0
- demo/seed/__init__.py +0 -0
- demo/seed/common.py +127 -0
- demo/seed/contributors.py +361 -0
- demo/seed/measurements.py +205 -0
- demo/seed/projects.py +565 -0
- demo/seed/record_search.py +318 -0
- demo/seed/samples.py +237 -0
- demo/static/scss/_bootstrap_vars.scss +1 -0
- demo/static/scss/stylesheet.scss +2 -0
- demo/tables.py +59 -0
- demo/templates/demo/rocksample_overview.html +32 -0
- demo/templates/demo/xrfmeasurement_overview.html +46 -0
- demo/templates/fairdm/landing.html +20 -0
- demo/tests/__init__.py +0 -0
- demo/tests/test_admin_views.py +465 -0
- demo/tests/test_api_smoke.py +54 -0
- demo/tests/test_contributors.py +136 -0
- demo/tests/test_forms.py +70 -0
- demo/tests/test_registry_api.py +219 -0
- fairdm/__init__.py +13 -0
- fairdm/api/__init__.py +1 -0
- fairdm/api/apps.py +22 -0
- fairdm/api/checks.py +70 -0
- fairdm/api/filters.py +503 -0
- fairdm/api/pagination.py +40 -0
- fairdm/api/permissions.py +103 -0
- fairdm/api/router.py +63 -0
- fairdm/api/schema.py +366 -0
- fairdm/api/serializers.py +1248 -0
- fairdm/api/settings.py +128 -0
- fairdm/api/throttling.py +56 -0
- fairdm/api/urls.py +23 -0
- fairdm/api/viewsets.py +408 -0
- fairdm/apps.py +129 -0
- fairdm/backups/__init__.py +1 -0
- fairdm/backups/apps.py +21 -0
- fairdm/backups/keys.py +111 -0
- fairdm/backups/management/__init__.py +0 -0
- fairdm/backups/management/commands/__init__.py +0 -0
- fairdm/backups/management/commands/backup.py +40 -0
- fairdm/backups/media.py +55 -0
- fairdm/backups/run.py +39 -0
- fairdm/backups/signals.py +33 -0
- fairdm/backups/tasks.py +21 -0
- fairdm/conf/__init__.py +101 -0
- fairdm/conf/addons.py +146 -0
- fairdm/conf/celery.py +10 -0
- fairdm/conf/checks.py +664 -0
- fairdm/conf/development.py +122 -0
- fairdm/conf/environment.py +62 -0
- fairdm/conf/settings/__init__.py +1 -0
- fairdm/conf/settings/addons.py +417 -0
- fairdm/conf/settings/api.py +44 -0
- fairdm/conf/settings/apps.py +192 -0
- fairdm/conf/settings/auth.py +93 -0
- fairdm/conf/settings/cache.py +37 -0
- fairdm/conf/settings/celery.py +40 -0
- fairdm/conf/settings/database.py +56 -0
- fairdm/conf/settings/email.py +39 -0
- fairdm/conf/settings/logging.py +72 -0
- fairdm/conf/settings/security.py +61 -0
- fairdm/conf/settings/static_media.py +108 -0
- fairdm/conf/setup.py +373 -0
- fairdm/conf/urls.py +74 -0
- fairdm/config.py +62 -0
- fairdm/contrib/__init__.py +9 -0
- fairdm/contrib/admin/__init__.py +1 -0
- fairdm/contrib/admin/admin.py +60 -0
- fairdm/contrib/admin/apps.py +17 -0
- fairdm/contrib/admin/sites.py +117 -0
- fairdm/contrib/admin/templates/admin/base_site.html +6 -0
- fairdm/contrib/admin/templates/admin/fixture_upload_form.html +14 -0
- fairdm/contrib/admin/urls.py +13 -0
- fairdm/contrib/admin/views.py +86 -0
- fairdm/contrib/autocomplete/__init__.py +3 -0
- fairdm/contrib/autocomplete/apps.py +12 -0
- fairdm/contrib/autocomplete/fields.py +671 -0
- fairdm/contrib/autocomplete/filters.py +94 -0
- fairdm/contrib/autocomplete/templates/autocomplete/contributor_option.html +18 -0
- fairdm/contrib/autocomplete/templates/autocomplete/record_option.html +8 -0
- fairdm/contrib/autocomplete/throttling.py +65 -0
- fairdm/contrib/autocomplete/urls.py +66 -0
- fairdm/contrib/autocomplete/views.py +1048 -0
- fairdm/contrib/collections/README.md +101 -0
- fairdm/contrib/collections/__init__.py +3 -0
- fairdm/contrib/collections/apps.py +62 -0
- fairdm/contrib/collections/tables.py +183 -0
- fairdm/contrib/collections/templates/collections/listing.html +33 -0
- fairdm/contrib/collections/urls.py +9 -0
- fairdm/contrib/collections/views.py +167 -0
- fairdm/contrib/contributors/__init__.py +1 -0
- fairdm/contrib/contributors/access.py +332 -0
- fairdm/contrib/contributors/adapters.py +136 -0
- fairdm/contrib/contributors/admin.py +790 -0
- fairdm/contrib/contributors/apps.py +34 -0
- fairdm/contrib/contributors/choices.py +67 -0
- fairdm/contrib/contributors/exceptions.py +9 -0
- fairdm/contrib/contributors/filters.py +100 -0
- fairdm/contrib/contributors/forms/__init__.py +1 -0
- fairdm/contrib/contributors/forms/account.py +79 -0
- fairdm/contrib/contributors/forms/contribution.py +186 -0
- fairdm/contrib/contributors/forms/forms.py +132 -0
- fairdm/contrib/contributors/forms/organization.py +151 -0
- fairdm/contrib/contributors/forms/person.py +84 -0
- fairdm/contrib/contributors/forms/profile.py +430 -0
- fairdm/contrib/contributors/forms/widgets.py +9 -0
- fairdm/contrib/contributors/managers.py +311 -0
- fairdm/contrib/contributors/migrations/0001_initial.py +616 -0
- fairdm/contrib/contributors/migrations/0002_delete_member_remove_person_is_member_and_more.py +22 -0
- fairdm/contrib/contributors/migrations/0003_alter_organization_country.py +23 -0
- fairdm/contrib/contributors/migrations/0004_delete_contributorrole_and_more.py +60 -0
- fairdm/contrib/contributors/migrations/0005_alter_contributor_profile.py +16 -0
- fairdm/contrib/contributors/migrations/0006_cleanup_duplicate_types.py +48 -0
- fairdm/contrib/contributors/migrations/0007_add_unique_type_constraints.py +17 -0
- fairdm/contrib/contributors/migrations/0008_migrate_to_location_model.py +67 -0
- fairdm/contrib/contributors/migrations/0009_alter_contributor_lang_and_more.py +26 -0
- fairdm/contrib/contributors/migrations/0010_alter_contributoridentifier_managers.py +15 -0
- fairdm/contrib/contributors/migrations/0011_add_partial_dates_and_privacy.py +60 -0
- fairdm/contrib/contributors/migrations/0012_rename_to_affiliation.py +15 -0
- fairdm/contrib/contributors/migrations/0013_update_related_names.py +43 -0
- fairdm/contrib/contributors/migrations/0014_add_is_claimed_field.py +19 -0
- fairdm/contrib/contributors/migrations/0015_set_is_claimed.py +25 -0
- fairdm/contrib/contributors/migrations/0016_make_is_claimed_non_nullable.py +18 -0
- fairdm/contrib/contributors/migrations/0017_remove_manage_organization_permission.py +81 -0
- fairdm/contrib/contributors/migrations/0018_claiming_audit_log.py +43 -0
- fairdm/contrib/contributors/migrations/0019_alter_contributoridentifier_type.py +17 -0
- fairdm/contrib/contributors/migrations/0020_contributors_audit.py +148 -0
- fairdm/contrib/contributors/migrations/0021_alter_affiliation_end_date_help_text.py +17 -0
- fairdm/contrib/contributors/migrations/0022_contribution_level.py +25 -0
- fairdm/contrib/contributors/migrations/0023_levels_from_stored_permissions.py +178 -0
- fairdm/contrib/contributors/migrations/0024_organization_location.py +29 -0
- fairdm/contrib/contributors/migrations/__init__.py +0 -0
- fairdm/contrib/contributors/models.py +2212 -0
- fairdm/contrib/contributors/permissions.py +115 -0
- fairdm/contrib/contributors/plugins/__init__.py +6 -0
- fairdm/contrib/contributors/plugins/organisation.py +1 -0
- fairdm/contrib/contributors/plugins/overview.py +295 -0
- fairdm/contrib/contributors/plugins/person.py +46 -0
- fairdm/contrib/contributors/plugins/shared.py +1017 -0
- fairdm/contrib/contributors/plugins/update.py +77 -0
- fairdm/contrib/contributors/profiles.py +119 -0
- fairdm/contrib/contributors/receivers.py +38 -0
- fairdm/contrib/contributors/resources.py +110 -0
- fairdm/contrib/contributors/services/__init__.py +1 -0
- fairdm/contrib/contributors/services/claiming.py +196 -0
- fairdm/contrib/contributors/services/crediting.py +374 -0
- fairdm/contrib/contributors/services/matching.py +44 -0
- fairdm/contrib/contributors/services/merge.py +215 -0
- fairdm/contrib/contributors/services/registries.py +441 -0
- fairdm/contrib/contributors/signals.py +48 -0
- fairdm/contrib/contributors/static/contributors/css/contributor-filter.css +32 -0
- fairdm/contrib/contributors/static/contributors/js/contributor-filter.js +79 -0
- fairdm/contrib/contributors/tasks.py +233 -0
- fairdm/contrib/contributors/templates/contributors/admin/change_form.html +49 -0
- fairdm/contrib/contributors/templates/contributors/admin/claim_person.html +77 -0
- fairdm/contrib/contributors/templates/contributors/admin/merge_person.html +70 -0
- fairdm/contrib/contributors/templates/contributors/claim_profile.html +66 -0
- fairdm/contrib/contributors/templates/contributors/contributor_card.html +5 -0
- fairdm/contrib/contributors/templates/contributors/organization_detail.html +49 -0
- fairdm/contrib/contributors/templates/contributors/overview/organization.html +196 -0
- fairdm/contrib/contributors/templates/contributors/overview/person.html +178 -0
- fairdm/contrib/contributors/templates/contributors/plugins/contribution_add_organization.html +189 -0
- fairdm/contrib/contributors/templates/contributors/plugins/contribution_add_person.html +201 -0
- fairdm/contrib/contributors/templates/contributors/plugins/contribution_edit.html +88 -0
- fairdm/contrib/contributors/templates/contributors/plugins/contribution_list.html +136 -0
- fairdm/contrib/contributors/templates/contributors/plugins/contribution_remove.html +66 -0
- fairdm/contrib/contributors/templates/contributors/plugins/update.html +20 -0
- fairdm/contrib/contributors/templates/contributors/team.html +14 -0
- fairdm/contrib/contributors/templates/cotton/card/affiliations/row.html +18 -0
- fairdm/contrib/contributors/templates/cotton/card/affiliations.html +31 -0
- fairdm/contrib/contributors/templates/cotton/card/hierarchy/node.html +14 -0
- fairdm/contrib/contributors/templates/cotton/card/hierarchy.html +52 -0
- fairdm/contrib/contributors/templates/cotton/card/links.html +23 -0
- fairdm/contrib/contributors/templates/cotton/card/records.html +51 -0
- fairdm/contrib/contributors/templates/cotton/card/roles.html +27 -0
- fairdm/contrib/contributors/templates/cotton/contribution/access.html +19 -0
- fairdm/contrib/contributors/templates/cotton/contribution/affiliation.html +44 -0
- fairdm/contrib/contributors/templates/cotton/contribution/controls.html +30 -0
- fairdm/contrib/contributors/templates/cotton/contribution/list_item.html +17 -0
- fairdm/contrib/contributors/templates/cotton/contribution/organization.html +38 -0
- fairdm/contrib/contributors/templates/cotton/contributor/affiliation.html +4 -0
- fairdm/contrib/contributors/templates/cotton/contributor/avatar.html +21 -0
- fairdm/contrib/contributors/templates/cotton/contributor/byline.html +28 -0
- fairdm/contrib/contributors/templates/cotton/contributor/card/contribution.html +21 -0
- fairdm/contrib/contributors/templates/cotton/contributor/card/index.html +12 -0
- fairdm/contrib/contributors/templates/cotton/contributor/card/organization.html +37 -0
- fairdm/contrib/contributors/templates/cotton/contributor/card/person.html +33 -0
- fairdm/contrib/contributors/templates/cotton/contributor/card/person_alt.html +24 -0
- fairdm/contrib/contributors/templates/cotton/contributor/icon.html +10 -0
- fairdm/contrib/contributors/templates/cotton/contributor/item.html +33 -0
- fairdm/contrib/contributors/templates/cotton/contributor/name.html +17 -0
- fairdm/contrib/contributors/templates/cotton/contributor/names.html +20 -0
- fairdm/contrib/contributors/templates/cotton/contributor/person/affiliation.html +9 -0
- fairdm/contrib/contributors/templates/cotton/contributor/person/affiliations.html +11 -0
- fairdm/contrib/contributors/templates/cotton/contributor/person/identifier.html +7 -0
- fairdm/contrib/contributors/templates/cotton/contributor/row.html +12 -0
- fairdm/contrib/contributors/templates/cotton/contributor/stack.html +23 -0
- fairdm/contrib/contributors/templates/cotton/contributor/summary.html +18 -0
- fairdm/contrib/contributors/templates/cotton/contributor_card.html +4 -0
- fairdm/contrib/contributors/templates/cotton/org/avatar.html +20 -0
- fairdm/contrib/contributors/templates/cotton/organization_card.html +16 -0
- fairdm/contrib/contributors/templates/cotton/person_card.html +23 -0
- fairdm/contrib/contributors/templates/widgets/orcid.html +77 -0
- fairdm/contrib/contributors/templatetags/__init__.py +1 -0
- fairdm/contrib/contributors/templatetags/contributor_tags.py +112 -0
- fairdm/contrib/contributors/urls.py +31 -0
- fairdm/contrib/contributors/utils/__init__.py +90 -0
- fairdm/contrib/contributors/utils/audit.py +47 -0
- fairdm/contrib/contributors/utils/helpers.py +94 -0
- fairdm/contrib/contributors/utils/tokens.py +77 -0
- fairdm/contrib/contributors/utils/transforms.py +958 -0
- fairdm/contrib/contributors/validators.py +223 -0
- fairdm/contrib/contributors/views/__init__.py +10 -0
- fairdm/contrib/contributors/views/claiming.py +111 -0
- fairdm/contrib/contributors/views/organization.py +34 -0
- fairdm/contrib/contributors/views/person.py +50 -0
- fairdm/contrib/contributors/views/team.py +73 -0
- fairdm/contrib/identity/__init__.py +1 -0
- fairdm/contrib/identity/admin.py +101 -0
- fairdm/contrib/identity/apps.py +12 -0
- fairdm/contrib/identity/forms.py +1 -0
- fairdm/contrib/identity/migrations/0001_initial.py +182 -0
- fairdm/contrib/identity/migrations/0002_rename_database_to_identity.py +159 -0
- fairdm/contrib/identity/migrations/0003_rename_databasetranslation_identitytranslation_and_more.py +27 -0
- fairdm/contrib/identity/migrations/0004_alter_authoritytranslation_unique_together_and_more.py +27 -0
- fairdm/contrib/identity/migrations/__init__.py +0 -0
- fairdm/contrib/identity/models.py +131 -0
- fairdm/contrib/identity/static/configuration/admin.css +28 -0
- fairdm/contrib/import_export/__init__.py +1 -0
- fairdm/contrib/import_export/apps.py +12 -0
- fairdm/contrib/import_export/formats.py +10 -0
- fairdm/contrib/import_export/forms.py +30 -0
- fairdm/contrib/import_export/resources.py +130 -0
- fairdm/contrib/import_export/templates/cotton/import_export/export_dropdown.html +21 -0
- fairdm/contrib/import_export/templates/cotton/import_export/result/base_errors.html +1 -0
- fairdm/contrib/import_export/templates/cotton/import_export/result/error.html +16 -0
- fairdm/contrib/import_export/templates/cotton/import_export/result/preview.html +1 -0
- fairdm/contrib/import_export/templates/cotton/import_export/result/validation_errors.html +1 -0
- fairdm/contrib/import_export/templates/import_export/import.html +122 -0
- fairdm/contrib/import_export/urls.py +5 -0
- fairdm/contrib/import_export/utils.py +174 -0
- fairdm/contrib/import_export/views.py +378 -0
- fairdm/contrib/location/__init__.py +1 -0
- fairdm/contrib/location/apps.py +12 -0
- fairdm/contrib/location/migrations/0001_initial.py +81 -0
- fairdm/contrib/location/migrations/0002_delete_point.py +16 -0
- fairdm/contrib/location/migrations/__init__.py +0 -0
- fairdm/contrib/plugins/__init__.py +73 -0
- fairdm/contrib/plugins/access.py +199 -0
- fairdm/contrib/plugins/apps.py +13 -0
- fairdm/contrib/plugins/base.py +265 -0
- fairdm/contrib/plugins/checks.py +220 -0
- fairdm/contrib/plugins/menus.py +16 -0
- fairdm/contrib/plugins/mixins.py +39 -0
- fairdm/contrib/plugins/registration.py +229 -0
- fairdm/contrib/plugins/templates/menus/tab.html +4 -0
- fairdm/contrib/plugins/templates/menus/tabs.html +10 -0
- fairdm/contrib/plugins/templatetags/__init__.py +1 -0
- fairdm/contrib/plugins/templatetags/plugin_tags.py +61 -0
- fairdm/contrib/plugins/utils.py +103 -0
- fairdm/core/__init__.py +1 -0
- fairdm/core/abstract.py +476 -0
- fairdm/core/admin.py +21 -0
- fairdm/core/apps.py +40 -0
- fairdm/core/choices.py +14 -0
- fairdm/core/dataset/__init__.py +1 -0
- fairdm/core/dataset/admin.py +190 -0
- fairdm/core/dataset/apps.py +13 -0
- fairdm/core/dataset/filters.py +90 -0
- fairdm/core/dataset/forms.py +164 -0
- fairdm/core/dataset/migrations/0001_initial.py +216 -0
- fairdm/core/dataset/migrations/0002_initial.py +92 -0
- fairdm/core/dataset/migrations/0003_alter_dataset_options.py +28 -0
- fairdm/core/dataset/migrations/0004_alter_dataset_reference.py +28 -0
- fairdm/core/dataset/migrations/0005_alter_dataset_license.py +28 -0
- fairdm/core/dataset/migrations/0006_cleanup_duplicate_types.py +61 -0
- fairdm/core/dataset/migrations/0007_add_unique_type_constraints.py +31 -0
- fairdm/core/dataset/migrations/0008_datasetliteraturerelation_alter_datasetdate_managers_and_more.py +193 -0
- fairdm/core/dataset/migrations/0009_alter_dataset_project_on_delete.py +32 -0
- fairdm/core/dataset/migrations/0010_add_dataset_permissions.py +30 -0
- fairdm/core/dataset/migrations/0011_alter_dataset_options_alter_dataset_license_and_more.py +52 -0
- fairdm/core/dataset/migrations/0012_dataset_published_alter_dataset_name.py +23 -0
- fairdm/core/dataset/migrations/0013_alter_dataset_options.py +17 -0
- fairdm/core/dataset/migrations/0014_alter_datasetdescription_value.py +19 -0
- fairdm/core/dataset/migrations/0015_index_modified.py +25 -0
- fairdm/core/dataset/migrations/__init__.py +0 -0
- fairdm/core/dataset/models.py +532 -0
- fairdm/core/dataset/plugins.py +707 -0
- fairdm/core/dataset/templates/cotton/dataset/card.html +145 -0
- fairdm/core/dataset/templates/dataset/dataset_card.html +2 -0
- fairdm/core/dataset/templates/dataset/dataset_detail.html +166 -0
- fairdm/core/dataset/templates/publishing/datacite/contributor.xml +14 -0
- fairdm/core/dataset/templates/publishing/datacite44.xml +106 -0
- fairdm/core/dataset/urls.py +18 -0
- fairdm/core/dataset/views.py +62 -0
- fairdm/core/dates.py +31 -0
- fairdm/core/descriptions.py +85 -0
- fairdm/core/editing.py +632 -0
- fairdm/core/filters.py +16 -0
- fairdm/core/forms.py +160 -0
- fairdm/core/formsets.py +68 -0
- fairdm/core/image_utils.py +32 -0
- fairdm/core/managers.py +162 -0
- fairdm/core/measurement/__init__.py +1 -0
- fairdm/core/measurement/admin.py +178 -0
- fairdm/core/measurement/apps.py +48 -0
- fairdm/core/measurement/config.py +94 -0
- fairdm/core/measurement/filters.py +130 -0
- fairdm/core/measurement/forms.py +145 -0
- fairdm/core/measurement/managers.py +160 -0
- fairdm/core/measurement/migrations/0001_initial.py +217 -0
- fairdm/core/measurement/migrations/0002_initial.py +69 -0
- fairdm/core/measurement/migrations/0003_alter_measurement_options.py +27 -0
- fairdm/core/measurement/migrations/0004_cleanup_duplicate_types.py +61 -0
- fairdm/core/measurement/migrations/0005_add_unique_type_constraints.py +31 -0
- fairdm/core/measurement/migrations/0006_alter_measurementdate_managers_and_more.py +37 -0
- fairdm/core/measurement/migrations/0007_alter_measurementdate_type_and_more.py +24 -0
- fairdm/core/measurement/migrations/0008_measurement_local_id.py +24 -0
- fairdm/core/measurement/migrations/0009_alter_measurementidentifier_type.py +19 -0
- fairdm/core/measurement/migrations/0010_alter_measurement_local_id.py +18 -0
- fairdm/core/measurement/migrations/0011_alter_measurement_sample.py +21 -0
- fairdm/core/measurement/migrations/0012_alter_measurement_name.py +18 -0
- fairdm/core/measurement/migrations/0013_alter_measurementdescription_value.py +19 -0
- fairdm/core/measurement/migrations/0014_index_modified.py +23 -0
- fairdm/core/measurement/migrations/0015_located_measurements.py +49 -0
- fairdm/core/measurement/migrations/__init__.py +0 -0
- fairdm/core/measurement/models.py +339 -0
- fairdm/core/measurement/plugins.py +293 -0
- fairdm/core/measurement/templates/measurement/measurement_overview.html +167 -0
- fairdm/core/measurement/urls.py +13 -0
- fairdm/core/models.py +13 -0
- fairdm/core/overview.py +134 -0
- fairdm/core/permissions.py +76 -0
- fairdm/core/plugins.py +573 -0
- fairdm/core/project/__init__.py +1 -0
- fairdm/core/project/admin.py +203 -0
- fairdm/core/project/apps.py +13 -0
- fairdm/core/project/filters.py +125 -0
- fairdm/core/project/forms.py +116 -0
- fairdm/core/project/migrations/0001_initial.py +277 -0
- fairdm/core/project/migrations/0002_alter_project_options.py +27 -0
- fairdm/core/project/migrations/0003_alter_projectdescription_type.py +19 -0
- fairdm/core/project/migrations/0004_cleanup_duplicate_types.py +63 -0
- fairdm/core/project/migrations/0005_add_unique_type_constraints.py +31 -0
- fairdm/core/project/migrations/0006_alter_project_options_alter_projectdate_options_and_more.py +82 -0
- fairdm/core/project/migrations/0007_project_created_by_alter_project_funding_and_more.py +39 -0
- fairdm/core/project/migrations/0008_convert_funding_to_datacite_shape.py +45 -0
- fairdm/core/project/migrations/0009_alter_project_name.py +18 -0
- fairdm/core/project/migrations/0010_alter_projectdescription_value.py +19 -0
- fairdm/core/project/migrations/0011_index_modified.py +23 -0
- fairdm/core/project/migrations/__init__.py +0 -0
- fairdm/core/project/models.py +393 -0
- fairdm/core/project/plugins.py +557 -0
- fairdm/core/project/receivers.py +5 -0
- fairdm/core/project/serializers.py +25 -0
- fairdm/core/project/templates/cotton/project/card.html +119 -0
- fairdm/core/project/templates/cotton/project/create_button.html +8 -0
- fairdm/core/project/templates/project/project_card.html +2 -0
- fairdm/core/project/templates/project/project_detail.html +193 -0
- fairdm/core/project/transforms.py +219 -0
- fairdm/core/project/urls.py +18 -0
- fairdm/core/project/validators.py +94 -0
- fairdm/core/project/views.py +61 -0
- fairdm/core/related_records.py +108 -0
- fairdm/core/sample/__init__.py +1 -0
- fairdm/core/sample/admin.py +211 -0
- fairdm/core/sample/apps.py +13 -0
- fairdm/core/sample/config.py +43 -0
- fairdm/core/sample/filters.py +138 -0
- fairdm/core/sample/forms.py +170 -0
- fairdm/core/sample/managers.py +187 -0
- fairdm/core/sample/migrations/0001_initial.py +325 -0
- fairdm/core/sample/migrations/0002_alter_sample_options.py +27 -0
- fairdm/core/sample/migrations/0003_cleanup_duplicate_types.py +61 -0
- fairdm/core/sample/migrations/0004_add_unique_type_constraints.py +31 -0
- fairdm/core/sample/migrations/0005_alter_sample_options_alter_samplerelation_options_and_more.py +73 -0
- fairdm/core/sample/migrations/0006_alter_sample_local_id.py +24 -0
- fairdm/core/sample/migrations/0007_alter_sample_status_alter_sample_uuid_and_more.py +31 -0
- fairdm/core/sample/migrations/0008_migrate_sample_status_to_unknown.py +23 -0
- fairdm/core/sample/migrations/0009_alter_sample_name.py +18 -0
- fairdm/core/sample/migrations/0010_alter_sampledescription_value.py +19 -0
- fairdm/core/sample/migrations/0011_index_modified.py +23 -0
- fairdm/core/sample/migrations/0012_located_samples.py +54 -0
- fairdm/core/sample/migrations/__init__.py +0 -0
- fairdm/core/sample/models.py +475 -0
- fairdm/core/sample/plugins.py +339 -0
- fairdm/core/sample/templates/cotton/sample/card.html +74 -0
- fairdm/core/sample/templates/sample/sample_detail.html +33 -0
- fairdm/core/sample/templates/sample/sample_overview.html +155 -0
- fairdm/core/sample/templates/sample/sample_type_detail.html +7 -0
- fairdm/core/sample/urls.py +13 -0
- fairdm/core/urls.py +10 -0
- fairdm/core/utils.py +232 -0
- fairdm/core/vocabularies.py +77 -0
- fairdm/db/__init__.py +1 -0
- fairdm/db/fields.py +61 -0
- fairdm/db/managers.py +1 -0
- fairdm/db/models.py +148 -0
- fairdm/factories/__init__.py +137 -0
- fairdm/factories/contributors.py +199 -0
- fairdm/factories/core.py +717 -0
- fairdm/factories/utils.py +146 -0
- fairdm/fixtures/django-waffle.json +35 -0
- fairdm/forms/__init__.py +10 -0
- fairdm/forms/base.py +227 -0
- fairdm/forms/fields.py +195 -0
- fairdm/management/__init__.py +1 -0
- fairdm/management/commands/__init__.py +1 -0
- fairdm/management/commands/cleanup_permissions.py +52 -0
- fairdm/management/commands/create_dev_accounts.py +165 -0
- fairdm/management/commands/generate_fake_data.py +522 -0
- fairdm/management/commands/seed_licenses.py +98 -0
- fairdm/management/commands/show_config.py +58 -0
- fairdm/management/commands/upgrade_vocabularies.py +385 -0
- fairdm/menus.py +137 -0
- fairdm/migrations/0001_initial.py +67 -0
- fairdm/migrations/0002_rename_taggeditem_table.py +36 -0
- fairdm/migrations/__init__.py +0 -0
- fairdm/models.py +14 -0
- fairdm/permissions.py +55 -0
- fairdm/plugins.py +6 -0
- fairdm/portal_roles.py +231 -0
- fairdm/registry/__init__.py +27 -0
- fairdm/registry/config.py +641 -0
- fairdm/registry/exceptions.py +176 -0
- fairdm/registry/factories.py +1153 -0
- fairdm/registry/registry.py +324 -0
- fairdm/static/brand/fairdm.svg +1 -0
- fairdm/static/brand/icon.png +0 -0
- fairdm/static/brand/icon.svg +90 -0
- fairdm/static/brand/icon_dark.svg +90 -0
- fairdm/static/brand/logo.svg +1 -0
- fairdm/static/brand/orcid_16x16.webp +0 -0
- fairdm/static/css/fairdm.css +402 -0
- fairdm/static/icons/user.svg +7 -0
- fairdm/static/img/ORCID.png +0 -0
- fairdm/static/img/brand/icon.svg +83 -0
- fairdm/static/img/brand/logo.svg +1 -0
- fairdm/static/js/chart-theme.js +68 -0
- fairdm/templates/api/docs/api_description.md +3 -0
- fairdm/templates/api/docs/dataset_list.md +11 -0
- fairdm/templates/api/docs/project_list.md +11 -0
- fairdm/templates/api/docs/sample_list.md +11 -0
- fairdm/templates/base.html +55 -0
- fairdm/templates/cards/compact.html +2 -0
- fairdm/templates/cards/large.html +46 -0
- fairdm/templates/client-side_cropping_widget.html +140 -0
- fairdm/templates/cotton/actions/create_new.html +20 -0
- fairdm/templates/cotton/actions/manage.html +21 -0
- fairdm/templates/cotton/actions/pending.html +21 -0
- fairdm/templates/cotton/card/citation.html +29 -0
- fairdm/templates/cotton/card/descriptions.html +35 -0
- fairdm/templates/cotton/card/details/entry.html +20 -0
- fairdm/templates/cotton/card/details.html +55 -0
- fairdm/templates/cotton/card/funding.html +41 -0
- fairdm/templates/cotton/card/identifiers.html +46 -0
- fairdm/templates/cotton/card/location.html +34 -0
- fairdm/templates/cotton/card/people.html +44 -0
- fairdm/templates/cotton/card/placeholder.html +20 -0
- fairdm/templates/cotton/card/readiness.html +49 -0
- fairdm/templates/cotton/card/timeline.html +45 -0
- fairdm/templates/cotton/components/object_card.html +68 -0
- fairdm/templates/cotton/date.html +10 -0
- fairdm/templates/cotton/entrance/aside.html +25 -0
- fairdm/templates/cotton/icons/orcid.html +4 -0
- fairdm/templates/cotton/landing/about.html +45 -0
- fairdm/templates/cotton/landing/api.html +29 -0
- fairdm/templates/cotton/landing/community.html +42 -0
- fairdm/templates/cotton/landing/datasets.html +19 -0
- fairdm/templates/cotton/landing/footer.html +33 -0
- fairdm/templates/cotton/landing/hero.html +41 -0
- fairdm/templates/cotton/landing/join.html +25 -0
- fairdm/templates/cotton/landing/projects.html +19 -0
- fairdm/templates/cotton/landing/stats.html +11 -0
- fairdm/templates/cotton/landing/type_list.html +32 -0
- fairdm/templates/cotton/landing/types.html +24 -0
- fairdm/templates/cotton/link.html +12 -0
- fairdm/templates/cotton/list/index.html +18 -0
- fairdm/templates/cotton/list/row.html +14 -0
- fairdm/templates/cotton/missing.html +8 -0
- fairdm/templates/cotton/nav/link.html +13 -0
- fairdm/templates/cotton/progress.html +21 -0
- fairdm/templates/cotton/stats/index.html +22 -0
- fairdm/templates/cotton/stats/item.html +34 -0
- fairdm/templates/cotton/tabs/index.html +22 -0
- fairdm/templates/cotton/tabs/tab.html +20 -0
- fairdm/templates/django_tomselect/render/option.html +57 -0
- fairdm/templates/django_tomselect/tomselect.html +63 -0
- fairdm/templates/editing/delete_record.html +38 -0
- fairdm/templates/editing/rows.html +6 -0
- fairdm/templates/fairdm/dashboard.html +1 -0
- fairdm/templates/fairdm/landing.html +78 -0
- fairdm/templates/fairdm/plugin.html +10 -0
- fairdm/templates/forms/checkbox.html +85 -0
- fairdm/templates/forms/fieldset.html +22 -0
- fairdm/templates/forms/widgets/array_select2.html +5 -0
- fairdm/templates/forms/widgets/quantity_field.html +31 -0
- fairdm/templates/forms/widgets/sortable_contributor.html +82 -0
- fairdm/templates/forms/widgets/sortable_contributor2.html +81 -0
- fairdm/templates/forms/widgets/xpostgres_array_widget.html +24 -0
- fairdm/templates/icons/activity.svg +3 -0
- fairdm/templates/icons/dashboard.svg +3 -0
- fairdm/templates/icons/facebook.svg +1 -0
- fairdm/templates/icons/filetype-xml.svg +3 -0
- fairdm/templates/icons/grid.svg +3 -0
- fairdm/templates/icons/linkedin.svg +1 -0
- fairdm/templates/icons/missing_image - Copy.svg +37 -0
- fairdm/templates/icons/missing_image.svg +4 -0
- fairdm/templates/icons/orcid/authenticated.svg +4 -0
- fairdm/templates/icons/orcid/readme.md +3 -0
- fairdm/templates/icons/orcid/unauthenticated.svg +5 -0
- fairdm/templates/icons/orcid.svg +16 -0
- fairdm/templates/icons/organization.svg +1 -0
- fairdm/templates/icons/ror.svg +12 -0
- fairdm/templates/icons/spinner.svg +1 -0
- fairdm/templates/icons/user.svg +7 -0
- fairdm/templates/icons/whatsapp.svg +1 -0
- fairdm/templates/icons/x_twitter.svg +1 -0
- fairdm/templates/layouts/base.html +12 -0
- fairdm/templates/location/widgets/location.html +54 -0
- fairdm/templates/mvp_accounts/entrance/base.html +34 -0
- fairdm/templates/overview/includes/map_library.html +30 -0
- fairdm/templates/overview/includes/type_badge.html +48 -0
- fairdm/templates/overview/page.html +196 -0
- fairdm/templates/pages/api/tos.html +71 -0
- fairdm/templates/pages/spectacular.html +26 -0
- fairdm/templates/research_vocabs/base.html +42 -0
- fairdm/templatetags/__init__.py +1 -0
- fairdm/templatetags/fairdm.py +327 -0
- fairdm/utils/__init__.py +90 -0
- fairdm/utils/choices.py +232 -0
- fairdm/utils/context_processors.py +62 -0
- fairdm/utils/inspection.py +535 -0
- fairdm/utils/location/__init__.py +1 -0
- fairdm/utils/location/coverage.py +78 -0
- fairdm/utils/location/db.py +53 -0
- fairdm/utils/location/forms.py +200 -0
- fairdm/utils/location/managers.py +139 -0
- fairdm/utils/location/resources.py +190 -0
- fairdm/utils/markdown.py +32 -0
- fairdm/utils/models.py +18 -0
- fairdm/utils/permissions.py +49 -0
- fairdm/utils/utils.py +233 -0
- fairdm/views/__init__.py +21 -0
- fairdm/views/base.py +322 -0
- fairdm/views/generic.py +239 -0
- fairdm/views/mixins.py +66 -0
- fairdm-0.3.0.dist-info/METADATA +506 -0
- fairdm-0.3.0.dist-info/RECORD +572 -0
- fairdm-0.3.0.dist-info/WHEEL +4 -0
- fairdm-0.3.0.dist-info/licenses/LICENSE +21 -0
demo/README.md
ADDED
|
@@ -0,0 +1,466 @@
|
|
|
1
|
+
# FairDM Demo Application
|
|
2
|
+
|
|
3
|
+
This is the reference implementation and demo portal for the FairDM framework. It demonstrates best practices for building research data portals using FairDM's plugin system, model configuration, and UI components.
|
|
4
|
+
|
|
5
|
+
## Purpose
|
|
6
|
+
|
|
7
|
+
The demo app serves multiple purposes:
|
|
8
|
+
|
|
9
|
+
1. **Reference Implementation**: Executable documentation showing how to use FairDM features
|
|
10
|
+
2. **Testing Ground**: Provides models and data for framework testing
|
|
11
|
+
3. **Learning Resource**: Examples of plugin patterns, model configuration, and UI composition
|
|
12
|
+
4. **Quick Start Template**: Starting point for new portal projects
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
## Plugin System Examples
|
|
17
|
+
|
|
18
|
+
The demo app showcases the FairDM plugin system with working examples. See [`demo/plugins.py`](plugins.py) for complete implementations.
|
|
19
|
+
|
|
20
|
+
### Basic Plugin Registration
|
|
21
|
+
|
|
22
|
+
```python
|
|
23
|
+
from fairdm import plugins
|
|
24
|
+
from fairdm.contrib.plugins import Plugin
|
|
25
|
+
from fairdm.core.plugins import BaseOverviewPlugin, BaseEditPlugin
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
@plugins.register(Sample)
|
|
29
|
+
class SampleOverview(BaseOverviewPlugin):
|
|
30
|
+
"""Basic overview inheriting standard behavior."""
|
|
31
|
+
|
|
32
|
+
def get_context_data(self, **kwargs):
|
|
33
|
+
context = super().get_context_data(**kwargs)
|
|
34
|
+
context["measurements"] = self.object.measurements.all()
|
|
35
|
+
return context
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
### Form-Based Edit Plugin
|
|
39
|
+
|
|
40
|
+
```python
|
|
41
|
+
@plugins.register(Sample)
|
|
42
|
+
class SampleEdit(BaseEditPlugin):
|
|
43
|
+
"""Edit form with custom permissions."""
|
|
44
|
+
|
|
45
|
+
form_class = SampleForm
|
|
46
|
+
menu = {"label": "Edit", "icon": "pencil", "order": 10}
|
|
47
|
+
permission = "demo.change_sample"
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
### Polymorphic Visibility (Conditional Plugins)
|
|
51
|
+
|
|
52
|
+
```python
|
|
53
|
+
@plugins.register(Sample)
|
|
54
|
+
class LocationDetailsPlugin(Plugin, TemplateView):
|
|
55
|
+
"""Only appears for samples with locations."""
|
|
56
|
+
|
|
57
|
+
menu = {"label": "Location Details", "icon": "geo-alt", "order": 50}
|
|
58
|
+
|
|
59
|
+
@staticmethod
|
|
60
|
+
def check(request, obj):
|
|
61
|
+
"""Custom visibility check."""
|
|
62
|
+
return obj and hasattr(obj, "location") and obj.location is not None
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### Plugin with Static Assets
|
|
66
|
+
|
|
67
|
+
The demo includes examples of plugins with CSS and JavaScript:
|
|
68
|
+
|
|
69
|
+
```python
|
|
70
|
+
@plugins.register(Sample)
|
|
71
|
+
class ChartPlugin(Plugin, TemplateView):
|
|
72
|
+
menu = {"label": "Charts", "icon": "chart", "order": 40}
|
|
73
|
+
|
|
74
|
+
class Media:
|
|
75
|
+
css = {"all": ("demo/charts.css",)}
|
|
76
|
+
js = (
|
|
77
|
+
"https://cdn.jsdelivr.net/npm/chart.js",
|
|
78
|
+
"demo/charts.js",
|
|
79
|
+
)
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
---
|
|
83
|
+
|
|
84
|
+
## Model Configuration
|
|
85
|
+
|
|
86
|
+
The demo app demonstrates custom Sample and Measurement types:
|
|
87
|
+
|
|
88
|
+
### Custom Sample Model
|
|
89
|
+
|
|
90
|
+
See [`demo/models/sample.py`](models/sample.py):
|
|
91
|
+
|
|
92
|
+
```python
|
|
93
|
+
class DemoSample(Sample):
|
|
94
|
+
"""Example Sample subclass with custom fields."""
|
|
95
|
+
|
|
96
|
+
char_field = models.CharField(max_length=100)
|
|
97
|
+
text_field = models.TextField()
|
|
98
|
+
date_field = models.DateField()
|
|
99
|
+
# ... more field examples
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
### Custom Measurement Model
|
|
103
|
+
|
|
104
|
+
See [`demo/models/measurement.py`](models/measurement.py):
|
|
105
|
+
|
|
106
|
+
```python
|
|
107
|
+
class DemoMeasurement(Measurement):
|
|
108
|
+
"""Example Measurement subclass."""
|
|
109
|
+
|
|
110
|
+
value = models.FloatField()
|
|
111
|
+
unit = models.CharField(max_length=50)
|
|
112
|
+
# ... more field examples
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
---
|
|
116
|
+
|
|
117
|
+
## Registry Configuration
|
|
118
|
+
|
|
119
|
+
The demo shows how to configure model display using the registry system.
|
|
120
|
+
|
|
121
|
+
See [`demo/options.py`](options.py):
|
|
122
|
+
|
|
123
|
+
```python
|
|
124
|
+
from fairdm.core.registry import registry
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
@registry.register(DemoSample)
|
|
128
|
+
class DemoSampleOptions:
|
|
129
|
+
list_fields = ["name", "char_field", "date_field"]
|
|
130
|
+
detail_fields = ["name", "char_field", "text_field", "date_field"]
|
|
131
|
+
filter_fields = ["char_field", "date_field"]
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
---
|
|
135
|
+
|
|
136
|
+
## Running the Demo
|
|
137
|
+
|
|
138
|
+
### 1. Create Migrations
|
|
139
|
+
|
|
140
|
+
```bash
|
|
141
|
+
uv run python manage.py makemigrations demo
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
### 2. Apply Migrations
|
|
145
|
+
|
|
146
|
+
```bash
|
|
147
|
+
uv run python manage.py migrate
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
### 3. Create Sample Data
|
|
151
|
+
|
|
152
|
+
```bash
|
|
153
|
+
uv run python manage.py shell
|
|
154
|
+
|
|
155
|
+
from demo.factories import DemoSampleFactory, DemoMeasurementFactory
|
|
156
|
+
|
|
157
|
+
# Create 10 demo samples
|
|
158
|
+
samples = DemoSampleFactory.create_batch(10)
|
|
159
|
+
|
|
160
|
+
# Create 5 measurements per sample
|
|
161
|
+
for sample in samples:
|
|
162
|
+
DemoMeasurementFactory.create_batch(5, sample=sample)
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
### 4. Run Development Server
|
|
166
|
+
|
|
167
|
+
```bash
|
|
168
|
+
uv run python manage.py runserver
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
### 5. Navigate to Demo Content
|
|
172
|
+
|
|
173
|
+
- **Projects**: http://localhost:8000/projects/
|
|
174
|
+
- **Datasets**: http://localhost:8000/datasets/
|
|
175
|
+
- **Samples**: http://localhost:8000/samples/
|
|
176
|
+
- **Measurements**: http://localhost:8000/measurements/
|
|
177
|
+
|
|
178
|
+
---
|
|
179
|
+
|
|
180
|
+
## Project Structure
|
|
181
|
+
|
|
182
|
+
```
|
|
183
|
+
demo/
|
|
184
|
+
├── README.md ← This file
|
|
185
|
+
├── apps.py ← App configuration
|
|
186
|
+
├── models.py ← Polymorphic Sample/Measurement models
|
|
187
|
+
├── plugins.py ← Example plugin implementations
|
|
188
|
+
├── options.py ← Registry configuration
|
|
189
|
+
├── factories.py ← Test data factories
|
|
190
|
+
├── forms.py ← Form definitions
|
|
191
|
+
├── tables.py ← django-tables2 table definitions
|
|
192
|
+
├── filters.py ← django-filter filterset definitions
|
|
193
|
+
├── tests/ ← Test suite
|
|
194
|
+
├── templates/ ← Demo-specific templates
|
|
195
|
+
│ └── demo/
|
|
196
|
+
│ └── plugins/ ← Plugin templates
|
|
197
|
+
└── static/ ← Demo-specific static assets
|
|
198
|
+
└── demo/
|
|
199
|
+
├── css/
|
|
200
|
+
└── js/
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
---
|
|
204
|
+
|
|
205
|
+
## Key Learning Examples
|
|
206
|
+
|
|
207
|
+
### 1. Plugin Inheritance Patterns
|
|
208
|
+
|
|
209
|
+
The demo shows three inheritance approaches:
|
|
210
|
+
|
|
211
|
+
- **Framework Base Classes**: Inherit from `BaseOverviewPlugin`, `BaseEditPlugin`, `BaseDeletePlugin`
|
|
212
|
+
- **Custom Base Classes**: Create reusable bases in your package
|
|
213
|
+
- **Direct Implementation**: Combine `Plugin` + Django CBV directly
|
|
214
|
+
|
|
215
|
+
See [`plugins.py`](plugins.py) for side-by-side examples.
|
|
216
|
+
|
|
217
|
+
### 2. Reusable Plugin Components
|
|
218
|
+
|
|
219
|
+
The demo demonstrates creating base plugins that portal developers can inherit:
|
|
220
|
+
|
|
221
|
+
```python
|
|
222
|
+
# Base class (reusable)
|
|
223
|
+
class GeologyAnalysisPlugin(Plugin, TemplateView):
|
|
224
|
+
menu = {"label": "Geology", "order": 30}
|
|
225
|
+
template_name = "geology/analysis.html"
|
|
226
|
+
|
|
227
|
+
def analyze_geology(self, sample):
|
|
228
|
+
"""Override in subclasses."""
|
|
229
|
+
return {}
|
|
230
|
+
|
|
231
|
+
|
|
232
|
+
# Portal-specific customization (inherits from base)
|
|
233
|
+
@plugins.register(Sample)
|
|
234
|
+
class CustomGeologyPlugin(GeologyAnalysisPlugin):
|
|
235
|
+
def analyze_geology(self, sample):
|
|
236
|
+
return {"custom_metric": sample.calculate_metric()}
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
### 3. Form-Based Plugins
|
|
240
|
+
|
|
241
|
+
Examples of plugins using Django's UpdateView, DeleteView, FormView:
|
|
242
|
+
|
|
243
|
+
- **Edit plugins**: Update object fields with crispy forms
|
|
244
|
+
- **Delete plugins**: Confirmation and cascade behavior
|
|
245
|
+
- **Wizard plugins**: Multi-step form workflows
|
|
246
|
+
|
|
247
|
+
### 4. List View Plugins
|
|
248
|
+
|
|
249
|
+
Show related objects in plugin tabs:
|
|
250
|
+
|
|
251
|
+
```python
|
|
252
|
+
@plugins.register(Project)
|
|
253
|
+
class ProjectDatasets(Plugin, DatasetListView):
|
|
254
|
+
menu = {"label": "Datasets", "icon": "dataset", "order": 20}
|
|
255
|
+
|
|
256
|
+
def get_queryset(self):
|
|
257
|
+
return self.object.datasets.all()
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
### 5. Permission-Based Plugins
|
|
261
|
+
|
|
262
|
+
Examples of:
|
|
263
|
+
- Model-level permissions (`permission = "app.change_model"`)
|
|
264
|
+
- Object-level permissions (django-guardian integration)
|
|
265
|
+
- Custom permission logic (`has_permission()` override)
|
|
266
|
+
|
|
267
|
+
---
|
|
268
|
+
|
|
269
|
+
## Testing
|
|
270
|
+
|
|
271
|
+
Run demo app tests:
|
|
272
|
+
|
|
273
|
+
```bash
|
|
274
|
+
# All tests
|
|
275
|
+
uv run pytest demo/tests/
|
|
276
|
+
|
|
277
|
+
# Specific test module
|
|
278
|
+
uv run pytest demo/tests/test_plugins.py
|
|
279
|
+
|
|
280
|
+
# With coverage
|
|
281
|
+
uv run pytest demo/tests/ --cov=demo
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
---
|
|
285
|
+
|
|
286
|
+
## Using Demo Code in Your Portal
|
|
287
|
+
|
|
288
|
+
The demo app code is designed to be copied and adapted:
|
|
289
|
+
|
|
290
|
+
1. **Copy plugin patterns** from `plugins.py` to your app
|
|
291
|
+
2. **Adapt model examples** from `models.py` to your domain
|
|
292
|
+
3. **Reuse factory patterns** from `factories.py` for test data
|
|
293
|
+
4. **Reference templates** in `templates/demo/plugins/`
|
|
294
|
+
|
|
295
|
+
### Example: Adapting a Plugin
|
|
296
|
+
|
|
297
|
+
From demo:
|
|
298
|
+
```python
|
|
299
|
+
@plugins.register(Sample)
|
|
300
|
+
class LocationDetailsPlugin(Plugin, TemplateView):
|
|
301
|
+
check = lambda r, o: o and hasattr(o, "location") and o.location
|
|
302
|
+
menu = {"label": "Location", "order": 50}
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
To your portal:
|
|
306
|
+
```python
|
|
307
|
+
@plugins.register(MySample)
|
|
308
|
+
class MyLocationPlugin(Plugin, TemplateView):
|
|
309
|
+
check = lambda r, o: o and o.has_geolocation()
|
|
310
|
+
menu = {"label": "Geolocation", "order": 50}
|
|
311
|
+
template_name = "myplugins/location.html"
|
|
312
|
+
|
|
313
|
+
def get_context_data(self, **kwargs):
|
|
314
|
+
context = super().get_context_data(**kwargs)
|
|
315
|
+
context["map_data"] = self.object.get_map_coordinates()
|
|
316
|
+
return context
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
---
|
|
320
|
+
|
|
321
|
+
## Contributor Features (Feature 009)
|
|
322
|
+
|
|
323
|
+
The demo app demonstrates contributor-related features including Person/Organization models, affiliation workflows, metadata transforms, and privacy controls.
|
|
324
|
+
|
|
325
|
+
### Person Model (AUTH_USER_MODEL)
|
|
326
|
+
|
|
327
|
+
FairDM uses `Person` as the `AUTH_USER_MODEL`. There are two patterns:
|
|
328
|
+
|
|
329
|
+
**Claimed Users** (interactive portal access):
|
|
330
|
+
```python
|
|
331
|
+
from fairdm.contrib.contributors.models import Person
|
|
332
|
+
|
|
333
|
+
# Create a claimed user
|
|
334
|
+
person = Person.objects.create_user(
|
|
335
|
+
email="researcher@example.com",
|
|
336
|
+
first_name="Jane",
|
|
337
|
+
last_name="Researcher",
|
|
338
|
+
password="secure_password",
|
|
339
|
+
)
|
|
340
|
+
assert person.is_claimed # True: has email, password, and is_active
|
|
341
|
+
```
|
|
342
|
+
|
|
343
|
+
**Unclaimed Contributors** (provenance-only records):
|
|
344
|
+
```python
|
|
345
|
+
# Create unclaimed contributor (no login capability)
|
|
346
|
+
unclaimed = Person.objects.create_unclaimed(
|
|
347
|
+
first_name="Historical",
|
|
348
|
+
last_name="Contributor",
|
|
349
|
+
)
|
|
350
|
+
assert not unclaimed.is_claimed # False: no email, no password, inactive
|
|
351
|
+
```
|
|
352
|
+
|
|
353
|
+
### Organization Ownership
|
|
354
|
+
|
|
355
|
+
Organizations use guardian permissions for ownership. The `Affiliation` type field determines permissions:
|
|
356
|
+
|
|
357
|
+
```python
|
|
358
|
+
from fairdm.contrib.contributors.models import Organization, Affiliation
|
|
359
|
+
|
|
360
|
+
org = Organization.objects.create(name="Research Lab")
|
|
361
|
+
person = Person.objects.get(email="researcher@example.com")
|
|
362
|
+
|
|
363
|
+
# Create OWNER-type affiliation (grants manage_organization permission automatically)
|
|
364
|
+
affiliation = Affiliation.objects.create(
|
|
365
|
+
person=person,
|
|
366
|
+
organization=org,
|
|
367
|
+
type=Affiliation.MembershipType.OWNER, # 3
|
|
368
|
+
)
|
|
369
|
+
|
|
370
|
+
# Permission granted via lifecycle hook
|
|
371
|
+
assert person.has_perm("contributors.manage_organization", org) # True
|
|
372
|
+
```
|
|
373
|
+
|
|
374
|
+
### Metadata Transforms
|
|
375
|
+
|
|
376
|
+
Export contributors to standard metadata formats:
|
|
377
|
+
|
|
378
|
+
```python
|
|
379
|
+
from fairdm.contrib.contributors.utils.transforms import (
|
|
380
|
+
DataCiteTransform,
|
|
381
|
+
SchemaOrgTransform,
|
|
382
|
+
CSLJSONTransform,
|
|
383
|
+
ORCIDTransform,
|
|
384
|
+
)
|
|
385
|
+
|
|
386
|
+
# Export Person to DataCite JSON
|
|
387
|
+
datacite_json = DataCiteTransform().export(person)
|
|
388
|
+
# Returns: {
|
|
389
|
+
# "givenName": "Jane",
|
|
390
|
+
# "familyName": "Researcher",
|
|
391
|
+
# "affiliation": [...]
|
|
392
|
+
# }
|
|
393
|
+
|
|
394
|
+
# Export Organization to Schema.org JSON-LD
|
|
395
|
+
schema_org_json = SchemaOrgTransform().export(org)
|
|
396
|
+
# Returns: {
|
|
397
|
+
# "@type": "Organization",
|
|
398
|
+
# "@id": "https://ror.org/...",
|
|
399
|
+
# "name": "Research Lab"
|
|
400
|
+
# }
|
|
401
|
+
|
|
402
|
+
# Export Person to CSL-JSON (for citations)
|
|
403
|
+
csl_json = CSLJSONTransform().export(person)
|
|
404
|
+
# Returns: {
|
|
405
|
+
# "type": "person",
|
|
406
|
+
# "given": "Jane",
|
|
407
|
+
# "family": "Researcher"
|
|
408
|
+
# }
|
|
409
|
+
```
|
|
410
|
+
|
|
411
|
+
Import from external identifiers:
|
|
412
|
+
|
|
413
|
+
```python
|
|
414
|
+
# Import from ORCID (async task)
|
|
415
|
+
person = ORCIDTransform().import_data(orcid_api_response)
|
|
416
|
+
|
|
417
|
+
# Import from ROR (async task)
|
|
418
|
+
org = Organization.from_ror("https://ror.org/04aj4c181")
|
|
419
|
+
```
|
|
420
|
+
|
|
421
|
+
### Privacy Controls
|
|
422
|
+
|
|
423
|
+
Control which fields are visible based on privacy settings:
|
|
424
|
+
|
|
425
|
+
```python
|
|
426
|
+
# Get visible fields for anonymous viewer
|
|
427
|
+
visible = person.get_visible_fields(viewer=None)
|
|
428
|
+
# Returns: {"name": "Jane Researcher", "affiliation": "..."}
|
|
429
|
+
# Email hidden by default for claimed users
|
|
430
|
+
|
|
431
|
+
# Privacy settings (JSONField)
|
|
432
|
+
person.privacy_settings = {
|
|
433
|
+
"email": "private", # Hide from everyone except self
|
|
434
|
+
"affiliation": "public", # Show to everyone
|
|
435
|
+
"orcid": "authenticated", # Show only to logged-in users
|
|
436
|
+
}
|
|
437
|
+
person.save()
|
|
438
|
+
|
|
439
|
+
# Check visibility for authenticated viewer
|
|
440
|
+
visible = person.get_visible_fields(viewer=some_authenticated_user)
|
|
441
|
+
# Returns: {"name": "...", "affiliation": "...", "orcid": "..."}
|
|
442
|
+
```
|
|
443
|
+
|
|
444
|
+
See [docs/developers/reference/contributor-models.md](../docs/developers/reference/contributor-models.md) for complete API reference.
|
|
445
|
+
|
|
446
|
+
---
|
|
447
|
+
|
|
448
|
+
## Contributing to Demo
|
|
449
|
+
|
|
450
|
+
The demo app is living documentation. When adding framework features:
|
|
451
|
+
|
|
452
|
+
1. **Add working examples** demonstrating the feature
|
|
453
|
+
2. **Include docstrings** explaining the purpose and pattern
|
|
454
|
+
3. **Link to documentation** with references to relevant guides
|
|
455
|
+
4. **Write tests** validating the example works correctly
|
|
456
|
+
|
|
457
|
+
See [Contributing Guide](../docs/contributing/index.md) for details.
|
|
458
|
+
|
|
459
|
+
---
|
|
460
|
+
|
|
461
|
+
## Further Reading
|
|
462
|
+
|
|
463
|
+
- [Plugin System Documentation](../docs/developers/how-to/create-a-plugin.md)
|
|
464
|
+
- [Framework Architecture](../specs/008-plugin-system/plan.md)
|
|
465
|
+
- [Quick Start Examples](../specs/008-plugin-system/quickstart.md)
|
|
466
|
+
- [API Reference](../docs/api/plugins.md)
|
demo/__init__.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Reference application showing how to build a portal on FairDM."""
|