netbox-data-import 2.0.0__tar.gz → 2.2.0__tar.gz
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.
- {netbox_data_import-2.0.0 → netbox_data_import-2.2.0}/PKG-INFO +2 -1
- netbox_data_import-2.2.0/netbox_data_import/__init__.py +49 -0
- {netbox_data_import-2.0.0 → netbox_data_import-2.2.0}/netbox_data_import/api/serializers.py +84 -2
- {netbox_data_import-2.0.0 → netbox_data_import-2.2.0}/netbox_data_import/api/urls.py +14 -1
- {netbox_data_import-2.0.0 → netbox_data_import-2.2.0}/netbox_data_import/api/views.py +85 -58
- {netbox_data_import-2.0.0 → netbox_data_import-2.2.0}/netbox_data_import/cable_target.py +203 -15
- {netbox_data_import-2.0.0 → netbox_data_import-2.2.0}/netbox_data_import/field_keys.py +9 -4
- {netbox_data_import-2.0.0 → netbox_data_import-2.2.0}/netbox_data_import/forms.py +27 -0
- {netbox_data_import-2.0.0 → netbox_data_import-2.2.0}/netbox_data_import/import_engine.py +3 -4
- netbox_data_import-2.2.0/netbox_data_import/inference_adapter.py +419 -0
- netbox_data_import-2.2.0/netbox_data_import/inference_backend.py +174 -0
- netbox_data_import-2.2.0/netbox_data_import/inference_connection_test.py +73 -0
- netbox_data_import-2.2.0/netbox_data_import/inference_credentials.py +296 -0
- netbox_data_import-2.2.0/netbox_data_import/inference_settings.py +303 -0
- netbox_data_import-2.2.0/netbox_data_import/inference_transport.py +130 -0
- netbox_data_import-2.2.0/netbox_data_import/inference_trust.py +230 -0
- {netbox_data_import-2.0.0 → netbox_data_import-2.2.0}/netbox_data_import/ip_assignment.py +2 -1
- {netbox_data_import-2.0.0 → netbox_data_import-2.2.0}/netbox_data_import/jobs.py +44 -2
- {netbox_data_import-2.0.0 → netbox_data_import-2.2.0}/netbox_data_import/migrations/0001_initial.py +4 -1
- {netbox_data_import-2.0.0 → netbox_data_import-2.2.0}/netbox_data_import/migrations/0022_migrate_profile_adapter_config.py +1 -1
- netbox_data_import-2.2.0/netbox_data_import/migrations/0031_inferencebackend.py +52 -0
- netbox_data_import-2.2.0/netbox_data_import/migrations/0032_inference_timeout_floor.py +24 -0
- netbox_data_import-2.2.0/netbox_data_import/migrations/0033_resolutionproposal.py +160 -0
- {netbox_data_import-2.0.0 → netbox_data_import-2.2.0}/netbox_data_import/models.py +333 -31
- {netbox_data_import-2.0.0 → netbox_data_import-2.2.0}/netbox_data_import/navigation.py +18 -0
- netbox_data_import-2.2.0/netbox_data_import/object_permissions.py +340 -0
- {netbox_data_import-2.0.0 → netbox_data_import-2.2.0}/netbox_data_import/plan.py +2 -2
- netbox_data_import-2.2.0/netbox_data_import/preview_row_actions.py +171 -0
- netbox_data_import-2.2.0/netbox_data_import/proposal_decisions.py +92 -0
- netbox_data_import-2.2.0/netbox_data_import/proposal_jobs.py +226 -0
- netbox_data_import-2.2.0/netbox_data_import/proposal_presentation.py +317 -0
- netbox_data_import-2.2.0/netbox_data_import/proposal_response.py +129 -0
- netbox_data_import-2.2.0/netbox_data_import/proposal_tasks.py +183 -0
- netbox_data_import-2.2.0/netbox_data_import/resolution_proposals.py +164 -0
- {netbox_data_import-2.0.0 → netbox_data_import-2.2.0}/netbox_data_import/review_workspace.py +190 -0
- {netbox_data_import-2.0.0 → netbox_data_import-2.2.0}/netbox_data_import/source_resolution.py +7 -5
- netbox_data_import-2.2.0/netbox_data_import/static/netbox_data_import/js/trace_proposals.js +200 -0
- netbox_data_import-2.2.0/netbox_data_import/static/netbox_data_import/js/trace_termination_picker.js +147 -0
- {netbox_data_import-2.0.0 → netbox_data_import-2.2.0}/netbox_data_import/tables.py +14 -0
- {netbox_data_import-2.0.0 → netbox_data_import-2.2.0}/netbox_data_import/target_modules.py +9 -9
- netbox_data_import-2.2.0/netbox_data_import/templates/netbox_data_import/_proposal_action.html +10 -0
- netbox_data_import-2.2.0/netbox_data_import/templates/netbox_data_import/_proposal_card.html +31 -0
- {netbox_data_import-2.0.0 → netbox_data_import-2.2.0}/netbox_data_import/templates/netbox_data_import/import_preview.html +8 -0
- netbox_data_import-2.2.0/netbox_data_import/templates/netbox_data_import/inferencebackend.html +95 -0
- netbox_data_import-2.2.0/netbox_data_import/templates/netbox_data_import/trace_workspace.html +341 -0
- netbox_data_import-2.2.0/netbox_data_import/termination_proposal.py +207 -0
- {netbox_data_import-2.0.0 → netbox_data_import-2.2.0}/netbox_data_import/tests/helpers.py +42 -3
- netbox_data_import-2.2.0/netbox_data_import/tests/inference_http.py +236 -0
- {netbox_data_import-2.0.0 → netbox_data_import-2.2.0}/netbox_data_import/tests/isolated_settings.py +2 -2
- netbox_data_import-2.2.0/netbox_data_import/tests/mock_discipline.py +560 -0
- {netbox_data_import-2.0.0 → netbox_data_import-2.2.0}/netbox_data_import/tests/test_adapter_config_migration.py +4 -4
- {netbox_data_import-2.0.0 → netbox_data_import-2.2.0}/netbox_data_import/tests/test_api.py +118 -0
- {netbox_data_import-2.0.0 → netbox_data_import-2.2.0}/netbox_data_import/tests/test_atomic_exits.py +3 -0
- {netbox_data_import-2.0.0 → netbox_data_import-2.2.0}/netbox_data_import/tests/test_cable_module.py +258 -4
- {netbox_data_import-2.0.0 → netbox_data_import-2.2.0}/netbox_data_import/tests/test_contact_resolution_ajax.py +5 -4
- {netbox_data_import-2.0.0 → netbox_data_import-2.2.0}/netbox_data_import/tests/test_devcontainer_e2e.py +2 -0
- {netbox_data_import-2.0.0 → netbox_data_import-2.2.0}/netbox_data_import/tests/test_device_module.py +3 -3
- netbox_data_import-2.2.0/netbox_data_import/tests/test_digest_indexed_models.py +181 -0
- {netbox_data_import-2.0.0 → netbox_data_import-2.2.0}/netbox_data_import/tests/test_execution_audit.py +2 -2
- {netbox_data_import-2.0.0 → netbox_data_import-2.2.0}/netbox_data_import/tests/test_import_cutover.py +25 -2
- {netbox_data_import-2.0.0 → netbox_data_import-2.2.0}/netbox_data_import/tests/test_import_plan.py +1 -1
- netbox_data_import-2.2.0/netbox_data_import/tests/test_inference_adapter.py +1119 -0
- netbox_data_import-2.2.0/netbox_data_import/tests/test_inference_backend.py +339 -0
- netbox_data_import-2.2.0/netbox_data_import/tests/test_inference_connection_test.py +452 -0
- netbox_data_import-2.2.0/netbox_data_import/tests/test_inference_credentials.py +527 -0
- netbox_data_import-2.2.0/netbox_data_import/tests/test_inference_secret_containment.py +216 -0
- netbox_data_import-2.2.0/netbox_data_import/tests/test_inference_settings.py +546 -0
- netbox_data_import-2.2.0/netbox_data_import/tests/test_inference_trust.py +255 -0
- netbox_data_import-2.2.0/netbox_data_import/tests/test_migrations.py +289 -0
- netbox_data_import-2.2.0/netbox_data_import/tests/test_mock_discipline.py +865 -0
- {netbox_data_import-2.0.0 → netbox_data_import-2.2.0}/netbox_data_import/tests/test_module_boundaries.py +1 -1
- netbox_data_import-2.2.0/netbox_data_import/tests/test_navigation.py +121 -0
- {netbox_data_import-2.0.0 → netbox_data_import-2.2.0}/netbox_data_import/tests/test_object_permission_writer.py +305 -1
- {netbox_data_import-2.0.0 → netbox_data_import-2.2.0}/netbox_data_import/tests/test_placement_reference_locks.py +3 -3
- {netbox_data_import-2.0.0 → netbox_data_import-2.2.0}/netbox_data_import/tests/test_preview_assets.py +2 -2
- netbox_data_import-2.2.0/netbox_data_import/tests/test_preview_state_ownership.py +254 -0
- netbox_data_import-2.2.0/netbox_data_import/tests/test_proposal_decisions.py +507 -0
- netbox_data_import-2.2.0/netbox_data_import/tests/test_proposal_jobs.py +678 -0
- netbox_data_import-2.2.0/netbox_data_import/tests/test_proposal_response.py +150 -0
- netbox_data_import-2.2.0/netbox_data_import/tests/test_proposal_tasks.py +216 -0
- netbox_data_import-2.2.0/netbox_data_import/tests/test_proposal_workspace.py +1352 -0
- {netbox_data_import-2.0.0 → netbox_data_import-2.2.0}/netbox_data_import/tests/test_quick_action_input_bounds.py +7 -7
- {netbox_data_import-2.0.0 → netbox_data_import-2.2.0}/netbox_data_import/tests/test_remove_device_type_creation_config_migration.py +2 -3
- netbox_data_import-2.2.0/netbox_data_import/tests/test_resolution_proposal_api.py +316 -0
- netbox_data_import-2.2.0/netbox_data_import/tests/test_resolution_proposals.py +526 -0
- {netbox_data_import-2.0.0 → netbox_data_import-2.2.0}/netbox_data_import/tests/test_review_actions.py +3 -3
- {netbox_data_import-2.0.0 → netbox_data_import-2.2.0}/netbox_data_import/tests/test_review_resolutions.py +2 -2
- {netbox_data_import-2.0.0 → netbox_data_import-2.2.0}/netbox_data_import/tests/test_standard_views.py +92 -2
- {netbox_data_import-2.0.0 → netbox_data_import-2.2.0}/netbox_data_import/tests/test_target_module_edge_contracts.py +1 -1
- {netbox_data_import-2.0.0 → netbox_data_import-2.2.0}/netbox_data_import/tests/test_trace_workbook.py +29 -0
- netbox_data_import-2.2.0/netbox_data_import/tests/test_trace_workspace.py +1708 -0
- {netbox_data_import-2.0.0 → netbox_data_import-2.2.0}/netbox_data_import/tests/test_views.py +69 -17
- {netbox_data_import-2.0.0 → netbox_data_import-2.2.0}/netbox_data_import/tests/test_views_coverage2.py +10 -4
- {netbox_data_import-2.0.0 → netbox_data_import-2.2.0}/netbox_data_import/trace_workbook.py +14 -8
- {netbox_data_import-2.0.0 → netbox_data_import-2.2.0}/netbox_data_import/urls.py +37 -1
- {netbox_data_import-2.0.0 → netbox_data_import-2.2.0}/netbox_data_import/views.py +853 -71
- {netbox_data_import-2.0.0 → netbox_data_import-2.2.0}/netbox_data_import.egg-info/PKG-INFO +2 -1
- {netbox_data_import-2.0.0 → netbox_data_import-2.2.0}/netbox_data_import.egg-info/SOURCES.txt +43 -0
- {netbox_data_import-2.0.0 → netbox_data_import-2.2.0}/netbox_data_import.egg-info/requires.txt +1 -0
- {netbox_data_import-2.0.0 → netbox_data_import-2.2.0}/pyproject.toml +46 -4
- netbox_data_import-2.0.0/netbox_data_import/__init__.py +0 -28
- netbox_data_import-2.0.0/netbox_data_import/object_permissions.py +0 -168
- netbox_data_import-2.0.0/netbox_data_import/preview_row_actions.py +0 -97
- netbox_data_import-2.0.0/netbox_data_import/tests/test_migrations.py +0 -137
- netbox_data_import-2.0.0/netbox_data_import/tests/test_navigation.py +0 -22
- {netbox_data_import-2.0.0 → netbox_data_import-2.2.0}/LICENSE +0 -0
- {netbox_data_import-2.0.0 → netbox_data_import-2.2.0}/README.md +0 -0
- {netbox_data_import-2.0.0 → netbox_data_import-2.2.0}/netbox_data_import/adapter_config.py +0 -0
- {netbox_data_import-2.0.0 → netbox_data_import-2.2.0}/netbox_data_import/adapter_forms.py +0 -0
- {netbox_data_import-2.0.0 → netbox_data_import-2.2.0}/netbox_data_import/adapters.py +0 -0
- {netbox_data_import-2.0.0 → netbox_data_import-2.2.0}/netbox_data_import/api/__init__.py +0 -0
- {netbox_data_import-2.0.0 → netbox_data_import-2.2.0}/netbox_data_import/catalog.py +0 -0
- {netbox_data_import-2.0.0 → netbox_data_import-2.2.0}/netbox_data_import/contact_resolution.py +0 -0
- {netbox_data_import-2.0.0 → netbox_data_import-2.2.0}/netbox_data_import/device_field_review.py +0 -0
- {netbox_data_import-2.0.0 → netbox_data_import-2.2.0}/netbox_data_import/device_identity.py +0 -0
- {netbox_data_import-2.0.0 → netbox_data_import-2.2.0}/netbox_data_import/filters.py +0 -0
- {netbox_data_import-2.0.0 → netbox_data_import-2.2.0}/netbox_data_import/flat_workbook.py +0 -0
- {netbox_data_import-2.0.0 → netbox_data_import-2.2.0}/netbox_data_import/graphql/__init__.py +0 -0
- {netbox_data_import-2.0.0 → netbox_data_import-2.2.0}/netbox_data_import/graphql/filters.py +0 -0
- {netbox_data_import-2.0.0 → netbox_data_import-2.2.0}/netbox_data_import/graphql/schema.py +0 -0
- {netbox_data_import-2.0.0 → netbox_data_import-2.2.0}/netbox_data_import/graphql/types.py +0 -0
- {netbox_data_import-2.0.0 → netbox_data_import-2.2.0}/netbox_data_import/migrations/0002_importjob.py +0 -0
- {netbox_data_import-2.0.0 → netbox_data_import-2.2.0}/netbox_data_import/migrations/0003_remove_classrolemapping_unique_class_role_mapping_per_profile_and_more.py +0 -0
- {netbox_data_import-2.0.0 → netbox_data_import-2.2.0}/netbox_data_import/migrations/0004_features.py +0 -0
- {netbox_data_import-2.0.0 → netbox_data_import-2.2.0}/netbox_data_import/migrations/0005_deviceexistingmatch.py +0 -0
- {netbox_data_import-2.0.0 → netbox_data_import-2.2.0}/netbox_data_import/migrations/0006_deviceexistingmatch_asset_tag.py +0 -0
- {netbox_data_import-2.0.0 → netbox_data_import-2.2.0}/netbox_data_import/migrations/0007_manufacturermapping.py +0 -0
- {netbox_data_import-2.0.0 → netbox_data_import-2.2.0}/netbox_data_import/migrations/0008_unique_constraints.py +0 -0
- {netbox_data_import-2.0.0 → netbox_data_import-2.2.0}/netbox_data_import/migrations/0009_add_rack_type_to_classrolemapping.py +0 -0
- {netbox_data_import-2.0.0 → netbox_data_import-2.2.0}/netbox_data_import/migrations/0010_alter_manufacturermapping_id.py +0 -0
- {netbox_data_import-2.0.0 → netbox_data_import-2.2.0}/netbox_data_import/migrations/0011_importprofile_capture_extra_data.py +0 -0
- {netbox_data_import-2.0.0 → netbox_data_import-2.2.0}/netbox_data_import/migrations/0012_classrolemapping_rack_type_related_name.py +0 -0
- {netbox_data_import-2.0.0 → netbox_data_import-2.2.0}/netbox_data_import/migrations/0013_remove_columnmapping_unique_target.py +0 -0
- {netbox_data_import-2.0.0 → netbox_data_import-2.2.0}/netbox_data_import/migrations/0014_alter_columnmapping_target_field_and_more.py +0 -0
- {netbox_data_import-2.0.0 → netbox_data_import-2.2.0}/netbox_data_import/migrations/0015_cleanup_duplicate_device_matches.py +0 -0
- {netbox_data_import-2.0.0 → netbox_data_import-2.2.0}/netbox_data_import/migrations/0016_deviceexistingmatch_ndi_devicematch_profile_device.py +0 -0
- {netbox_data_import-2.0.0 → netbox_data_import-2.2.0}/netbox_data_import/migrations/0017_native_contact_sync.py +0 -0
- {netbox_data_import-2.0.0 → netbox_data_import-2.2.0}/netbox_data_import/migrations/0018_ignoredfielddifference.py +0 -0
- {netbox_data_import-2.0.0 → netbox_data_import-2.2.0}/netbox_data_import/migrations/0019_deviceimportsource.py +0 -0
- {netbox_data_import-2.0.0 → netbox_data_import-2.2.0}/netbox_data_import/migrations/0020_migrate_import_source_custom_field.py +0 -0
- {netbox_data_import-2.0.0 → netbox_data_import-2.2.0}/netbox_data_import/migrations/0021_importprofile_adapter_config.py +0 -0
- {netbox_data_import-2.0.0 → netbox_data_import-2.2.0}/netbox_data_import/migrations/0023_drop_moved_profile_columns.py +0 -0
- {netbox_data_import-2.0.0 → netbox_data_import-2.2.0}/netbox_data_import/migrations/0024_rename_importjob_importexecution.py +0 -0
- {netbox_data_import-2.0.0 → netbox_data_import-2.2.0}/netbox_data_import/migrations/0025_remove_importexecution_dry_run_and_more.py +0 -0
- {netbox_data_import-2.0.0 → netbox_data_import-2.2.0}/netbox_data_import/migrations/0026_alter_sourcedocument_profile.py +0 -0
- {netbox_data_import-2.0.0 → netbox_data_import-2.2.0}/netbox_data_import/migrations/0027_cableclassmapping_terminationresolution.py +0 -0
- {netbox_data_import-2.0.0 → netbox_data_import-2.2.0}/netbox_data_import/migrations/0028_cableimportsource.py +0 -0
- {netbox_data_import-2.0.0 → netbox_data_import-2.2.0}/netbox_data_import/migrations/0029_alter_cableimportsource_from_text_and_more.py +0 -0
- {netbox_data_import-2.0.0 → netbox_data_import-2.2.0}/netbox_data_import/migrations/0030_remove_device_type_creation_config.py +0 -0
- {netbox_data_import-2.0.0 → netbox_data_import-2.2.0}/netbox_data_import/migrations/__init__.py +0 -0
- {netbox_data_import-2.0.0 → netbox_data_import-2.2.0}/netbox_data_import/netbox_reader.py +0 -0
- {netbox_data_import-2.0.0 → netbox_data_import-2.2.0}/netbox_data_import/signals.py +0 -0
- {netbox_data_import-2.0.0 → netbox_data_import-2.2.0}/netbox_data_import/static/netbox_data_import/js/conflict_modal.js +0 -0
- {netbox_data_import-2.0.0 → netbox_data_import-2.2.0}/netbox_data_import/static/netbox_data_import/js/contact_candidate_modal.js +0 -0
- {netbox_data_import-2.0.0 → netbox_data_import-2.2.0}/netbox_data_import/static/netbox_data_import/js/preview_row_actions.js +0 -0
- {netbox_data_import-2.0.0 → netbox_data_import-2.2.0}/netbox_data_import/static/netbox_data_import/js/preview_row_controls.js +0 -0
- {netbox_data_import-2.0.0 → netbox_data_import-2.2.0}/netbox_data_import/static/netbox_data_import/js/split_name_modal.js +0 -0
- {netbox_data_import-2.0.0 → netbox_data_import-2.2.0}/netbox_data_import/static/netbox_data_import/js/sync_row_modal.js +0 -0
- {netbox_data_import-2.0.0 → netbox_data_import-2.2.0}/netbox_data_import/target_runtime.py +0 -0
- {netbox_data_import-2.0.0 → netbox_data_import-2.2.0}/netbox_data_import/template_content.py +0 -0
- {netbox_data_import-2.0.0 → netbox_data_import-2.2.0}/netbox_data_import/templates/netbox_data_import/_import_progress.html +0 -0
- {netbox_data_import-2.0.0 → netbox_data_import-2.2.0}/netbox_data_import/templates/netbox_data_import/analysis.html +0 -0
- {netbox_data_import-2.0.0 → netbox_data_import-2.2.0}/netbox_data_import/templates/netbox_data_import/bulk_yaml_import.html +0 -0
- {netbox_data_import-2.0.0 → netbox_data_import-2.2.0}/netbox_data_import/templates/netbox_data_import/cableclassmapping_edit.html +0 -0
- {netbox_data_import-2.0.0 → netbox_data_import-2.2.0}/netbox_data_import/templates/netbox_data_import/classrolemapping_edit.html +0 -0
- {netbox_data_import-2.0.0 → netbox_data_import-2.2.0}/netbox_data_import/templates/netbox_data_import/columnmapping_edit.html +0 -0
- {netbox_data_import-2.0.0 → netbox_data_import-2.2.0}/netbox_data_import/templates/netbox_data_import/columntransformrule_edit.html +0 -0
- {netbox_data_import-2.0.0 → netbox_data_import-2.2.0}/netbox_data_import/templates/netbox_data_import/device_import_data.html +0 -0
- {netbox_data_import-2.0.0 → netbox_data_import-2.2.0}/netbox_data_import/templates/netbox_data_import/devicetypemapping_edit.html +0 -0
- {netbox_data_import-2.0.0 → netbox_data_import-2.2.0}/netbox_data_import/templates/netbox_data_import/import_profile_yaml.html +0 -0
- {netbox_data_import-2.0.0 → netbox_data_import-2.2.0}/netbox_data_import/templates/netbox_data_import/import_progress.html +0 -0
- {netbox_data_import-2.0.0 → netbox_data_import-2.2.0}/netbox_data_import/templates/netbox_data_import/import_results.html +0 -0
- {netbox_data_import-2.0.0 → netbox_data_import-2.2.0}/netbox_data_import/templates/netbox_data_import/import_setup.html +0 -0
- {netbox_data_import-2.0.0 → netbox_data_import-2.2.0}/netbox_data_import/templates/netbox_data_import/importexecution_list.html +0 -0
- {netbox_data_import-2.0.0 → netbox_data_import-2.2.0}/netbox_data_import/templates/netbox_data_import/importprofile.html +0 -0
- {netbox_data_import-2.0.0 → netbox_data_import-2.2.0}/netbox_data_import/templates/netbox_data_import/importprofile_list.html +0 -0
- {netbox_data_import-2.0.0 → netbox_data_import-2.2.0}/netbox_data_import/templates/netbox_data_import/source_resolution_list.html +0 -0
- {netbox_data_import-2.0.0 → netbox_data_import-2.2.0}/netbox_data_import/tests/__init__.py +0 -0
- {netbox_data_import-2.0.0 → netbox_data_import-2.2.0}/netbox_data_import/tests/conftest.py +0 -0
- {netbox_data_import-2.0.0 → netbox_data_import-2.2.0}/netbox_data_import/tests/fixtures/combined_name.xlsx +0 -0
- {netbox_data_import-2.0.0 → netbox_data_import-2.2.0}/netbox_data_import/tests/fixtures/sample_cans.xlsx +0 -0
- {netbox_data_import-2.0.0 → netbox_data_import-2.2.0}/netbox_data_import/tests/fixtures/trace_copper.xlsx +0 -0
- {netbox_data_import-2.0.0 → netbox_data_import-2.2.0}/netbox_data_import/tests/fixtures/trace_fiber.xlsx +0 -0
- {netbox_data_import-2.0.0 → netbox_data_import-2.2.0}/netbox_data_import/tests/mixins.py +0 -0
- {netbox_data_import-2.0.0 → netbox_data_import-2.2.0}/netbox_data_import/tests/netbox_configuration.py +0 -0
- {netbox_data_import-2.0.0 → netbox_data_import-2.2.0}/netbox_data_import/tests/parallel.py +0 -0
- {netbox_data_import-2.0.0 → netbox_data_import-2.2.0}/netbox_data_import/tests/test_adapter_config.py +0 -0
- {netbox_data_import-2.0.0 → netbox_data_import-2.2.0}/netbox_data_import/tests/test_ci_configuration.py +0 -0
- {netbox_data_import-2.0.0 → netbox_data_import-2.2.0}/netbox_data_import/tests/test_comment_style.py +0 -0
- {netbox_data_import-2.0.0 → netbox_data_import-2.2.0}/netbox_data_import/tests/test_contact_role_suggestion.py +0 -0
- {netbox_data_import-2.0.0 → netbox_data_import-2.2.0}/netbox_data_import/tests/test_contact_sync.py +0 -0
- {netbox_data_import-2.0.0 → netbox_data_import-2.2.0}/netbox_data_import/tests/test_cutover_edge_contracts.py +0 -0
- {netbox_data_import-2.0.0 → netbox_data_import-2.2.0}/netbox_data_import/tests/test_graphql_filters.py +0 -0
- {netbox_data_import-2.0.0 → netbox_data_import-2.2.0}/netbox_data_import/tests/test_import_engine.py +0 -0
- {netbox_data_import-2.0.0 → netbox_data_import-2.2.0}/netbox_data_import/tests/test_import_execution.py +0 -0
- {netbox_data_import-2.0.0 → netbox_data_import-2.2.0}/netbox_data_import/tests/test_import_source_migration.py +0 -0
- {netbox_data_import-2.0.0 → netbox_data_import-2.2.0}/netbox_data_import/tests/test_installation_docs.py +0 -0
- {netbox_data_import-2.0.0 → netbox_data_import-2.2.0}/netbox_data_import/tests/test_ip_target.py +0 -0
- {netbox_data_import-2.0.0 → netbox_data_import-2.2.0}/netbox_data_import/tests/test_legacy_profile_yaml.py +0 -0
- {netbox_data_import-2.0.0 → netbox_data_import-2.2.0}/netbox_data_import/tests/test_misc_coverage.py +0 -0
- {netbox_data_import-2.0.0 → netbox_data_import-2.2.0}/netbox_data_import/tests/test_model_base_conventions.py +0 -0
- {netbox_data_import-2.0.0 → netbox_data_import-2.2.0}/netbox_data_import/tests/test_models.py +0 -0
- {netbox_data_import-2.0.0 → netbox_data_import-2.2.0}/netbox_data_import/tests/test_netbox_reader.py +0 -0
- {netbox_data_import-2.0.0 → netbox_data_import-2.2.0}/netbox_data_import/tests/test_parallel_test_setup.py +0 -0
- {netbox_data_import-2.0.0 → netbox_data_import-2.2.0}/netbox_data_import/tests/test_pristine_preview_rows.py +0 -0
- {netbox_data_import-2.0.0 → netbox_data_import-2.2.0}/netbox_data_import/tests/test_profile_policy_crud_scoping.py +0 -0
- {netbox_data_import-2.0.0 → netbox_data_import-2.2.0}/netbox_data_import/tests/test_quick_action_object_permissions.py +0 -0
- {netbox_data_import-2.0.0 → netbox_data_import-2.2.0}/netbox_data_import/tests/test_quick_action_profile_id.py +0 -0
- {netbox_data_import-2.0.0 → netbox_data_import-2.2.0}/netbox_data_import/tests/test_rack_module.py +0 -0
- {netbox_data_import-2.0.0 → netbox_data_import-2.2.0}/netbox_data_import/tests/test_release_metadata.py +0 -0
- {netbox_data_import-2.0.0 → netbox_data_import-2.2.0}/netbox_data_import/tests/test_review_workspace.py +0 -0
- {netbox_data_import-2.0.0 → netbox_data_import-2.2.0}/netbox_data_import/tests/test_serial_display.py +0 -0
- {netbox_data_import-2.0.0 → netbox_data_import-2.2.0}/netbox_data_import/tests/test_source_adapter.py +0 -0
- {netbox_data_import-2.0.0 → netbox_data_import-2.2.0}/netbox_data_import/tests/test_target_field_catalog.py +0 -0
- {netbox_data_import-2.0.0 → netbox_data_import-2.2.0}/netbox_data_import/tests/test_trace_decisions.py +0 -0
- {netbox_data_import-2.0.0 → netbox_data_import-2.2.0}/netbox_data_import/tests/test_views_extra.py +0 -0
- {netbox_data_import-2.0.0 → netbox_data_import-2.2.0}/netbox_data_import/trace_schema.py +0 -0
- {netbox_data_import-2.0.0 → netbox_data_import-2.2.0}/netbox_data_import/transform_regex.py +0 -0
- {netbox_data_import-2.0.0 → netbox_data_import-2.2.0}/netbox_data_import/utils.py +0 -0
- {netbox_data_import-2.0.0 → netbox_data_import-2.2.0}/netbox_data_import/values.py +2 -2
- {netbox_data_import-2.0.0 → netbox_data_import-2.2.0}/netbox_data_import.egg-info/dependency_links.txt +0 -0
- {netbox_data_import-2.0.0 → netbox_data_import-2.2.0}/netbox_data_import.egg-info/top_level.txt +0 -0
- {netbox_data_import-2.0.0 → netbox_data_import-2.2.0}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: netbox-data-import
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.2.0
|
|
4
4
|
Summary: NetBox plugin for importing data from external DCIM systems
|
|
5
5
|
Author: Marcin Zieba
|
|
6
6
|
License-Expression: Apache-2.0
|
|
@@ -18,6 +18,7 @@ Requires-Dist: defusedxml>=0.7.1
|
|
|
18
18
|
Requires-Dist: google-re2>=1.1.20251105
|
|
19
19
|
Requires-Dist: openpyxl>=3.1
|
|
20
20
|
Requires-Dist: pyyaml>=6
|
|
21
|
+
Requires-Dist: requests>=2.33.0
|
|
21
22
|
Dynamic: license-file
|
|
22
23
|
|
|
23
24
|
<!--
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
# Copyright (C) 2025 Marcin Zieba <marcinpsk@gmail.com>
|
|
3
|
+
from typing import Any
|
|
4
|
+
|
|
5
|
+
from netbox.plugins import PluginConfig
|
|
6
|
+
|
|
7
|
+
__version__ = "2.2.0"
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class NetBoxDataImportConfig(PluginConfig):
|
|
11
|
+
"""NetBox plugin configuration for the Data Import plugin."""
|
|
12
|
+
|
|
13
|
+
name = "netbox_data_import"
|
|
14
|
+
verbose_name = "NetBox Data Import"
|
|
15
|
+
description = "NetBox plugin for importing data from external DCIM systems"
|
|
16
|
+
version = __version__
|
|
17
|
+
base_url = "data-import"
|
|
18
|
+
author = "Marcin Zieba"
|
|
19
|
+
author_email = "marcinpsk@gmail.com"
|
|
20
|
+
min_version = "4.6.0"
|
|
21
|
+
graphql_schema = "graphql.schema.schema"
|
|
22
|
+
|
|
23
|
+
default_settings: dict[str, Any] = {
|
|
24
|
+
# A deployment that names no allowlist reaches no origin, rather than every origin.
|
|
25
|
+
"inference_backend_origin_allowlist": [],
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
@classmethod
|
|
29
|
+
def validate(cls, user_config, netbox_version):
|
|
30
|
+
"""Reject a malformed Inference Backend configuration before the application serves a request."""
|
|
31
|
+
super().validate(user_config, netbox_version)
|
|
32
|
+
|
|
33
|
+
from django.core.exceptions import ImproperlyConfigured
|
|
34
|
+
|
|
35
|
+
from .inference_settings import InvalidInferenceConfiguration, validate_plugin_settings
|
|
36
|
+
|
|
37
|
+
try:
|
|
38
|
+
validate_plugin_settings(user_config)
|
|
39
|
+
except InvalidInferenceConfiguration as exc:
|
|
40
|
+
raise ImproperlyConfigured(f"Plugin {cls.__module__} has an invalid configuration: {exc}") from exc
|
|
41
|
+
|
|
42
|
+
def ready(self):
|
|
43
|
+
"""Import the modules NetBox does not load, so their job and task registrations run."""
|
|
44
|
+
super().ready()
|
|
45
|
+
|
|
46
|
+
from . import jobs, termination_proposal
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
config = NetBoxDataImportConfig
|
|
@@ -19,12 +19,20 @@ from ..models import (
|
|
|
19
19
|
ColumnTransformRule,
|
|
20
20
|
SourceResolution,
|
|
21
21
|
ImportExecution,
|
|
22
|
+
InferenceBackend,
|
|
23
|
+
ResolutionProposal,
|
|
22
24
|
validate_adapter_target_module,
|
|
23
25
|
validate_contact_candidate_resolution,
|
|
24
26
|
validate_section_applicability,
|
|
25
27
|
)
|
|
26
28
|
|
|
27
29
|
|
|
30
|
+
def _validate_candidate_values(candidate_values):
|
|
31
|
+
"""Require a JSON object at the Contact candidate boundary."""
|
|
32
|
+
if not isinstance(candidate_values, dict):
|
|
33
|
+
raise TypeError("Contact candidate values must be a JSON object.")
|
|
34
|
+
|
|
35
|
+
|
|
28
36
|
class PolicySectionApplicabilityMixin:
|
|
29
37
|
"""Apply the shared policy-section applicability rule on the REST write path."""
|
|
30
38
|
|
|
@@ -222,8 +230,7 @@ class SourceResolutionSerializer(PolicySectionSerializer):
|
|
|
222
230
|
resolved_fields = attrs.get("resolved_fields", getattr(instance, "resolved_fields", None))
|
|
223
231
|
try:
|
|
224
232
|
candidate_values = json.loads(original_value)
|
|
225
|
-
|
|
226
|
-
raise ValueError
|
|
233
|
+
_validate_candidate_values(candidate_values)
|
|
227
234
|
configured_sources = profile.column_mappings.filter(target_field="candidate:contact").values_list(
|
|
228
235
|
"source_column", flat=True
|
|
229
236
|
)
|
|
@@ -273,3 +280,78 @@ class ImportExecutionSerializer(serializers.ModelSerializer):
|
|
|
273
280
|
"failure_detail",
|
|
274
281
|
]
|
|
275
282
|
read_only_fields = fields
|
|
283
|
+
|
|
284
|
+
|
|
285
|
+
class ResolutionProposalSerializer(serializers.ModelSerializer):
|
|
286
|
+
"""Read-only serializer for the Resolution Proposal audit record."""
|
|
287
|
+
|
|
288
|
+
class Meta:
|
|
289
|
+
model = ResolutionProposal
|
|
290
|
+
fields = [
|
|
291
|
+
"id",
|
|
292
|
+
"profile",
|
|
293
|
+
"task_type",
|
|
294
|
+
"field_key",
|
|
295
|
+
"status",
|
|
296
|
+
"source_evidence",
|
|
297
|
+
"resolved_device_type",
|
|
298
|
+
"resolved_device_id",
|
|
299
|
+
"prompt_version",
|
|
300
|
+
"response_schema_version",
|
|
301
|
+
"candidate_snapshot",
|
|
302
|
+
"requested_by",
|
|
303
|
+
"created",
|
|
304
|
+
"last_updated",
|
|
305
|
+
"outcome",
|
|
306
|
+
"selected_candidate_id",
|
|
307
|
+
"selected_object_type",
|
|
308
|
+
"selected_object_id",
|
|
309
|
+
"explanation",
|
|
310
|
+
"backend_metadata",
|
|
311
|
+
"response_diagnostic",
|
|
312
|
+
"failure_reason",
|
|
313
|
+
"decision",
|
|
314
|
+
"decided_by",
|
|
315
|
+
"decided_at",
|
|
316
|
+
"written_resolution",
|
|
317
|
+
]
|
|
318
|
+
read_only_fields = fields
|
|
319
|
+
|
|
320
|
+
|
|
321
|
+
class ResolutionProposalHistorySerializer(serializers.ModelSerializer):
|
|
322
|
+
"""Serialize one attempt summary for profile-scoped field history."""
|
|
323
|
+
|
|
324
|
+
class Meta:
|
|
325
|
+
model = ResolutionProposal
|
|
326
|
+
fields = ["id", "created", "status", "outcome", "decision", "failure_reason"]
|
|
327
|
+
read_only_fields = fields
|
|
328
|
+
|
|
329
|
+
|
|
330
|
+
class InferenceBackendSerializer(NetBoxModelSerializer):
|
|
331
|
+
"""Serialize one Inference Backend row, without its credential reference.
|
|
332
|
+
|
|
333
|
+
NetBox resolves this class by model name to freeze a delete event payload.
|
|
334
|
+
"""
|
|
335
|
+
|
|
336
|
+
class Meta:
|
|
337
|
+
model = InferenceBackend
|
|
338
|
+
fields = [
|
|
339
|
+
"id",
|
|
340
|
+
"url",
|
|
341
|
+
"display",
|
|
342
|
+
"backend_key",
|
|
343
|
+
"display_name",
|
|
344
|
+
"adapter_type",
|
|
345
|
+
"api_root",
|
|
346
|
+
"model",
|
|
347
|
+
"authentication",
|
|
348
|
+
"response_mode",
|
|
349
|
+
"connect_timeout",
|
|
350
|
+
"read_timeout",
|
|
351
|
+
"enabled",
|
|
352
|
+
"tags",
|
|
353
|
+
"custom_fields",
|
|
354
|
+
"created",
|
|
355
|
+
"last_updated",
|
|
356
|
+
]
|
|
357
|
+
brief_fields = ["id", "url", "display", "backend_key", "display_name", "enabled"]
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
# Copyright (C) 2025 Marcin Zieba <marcinpsk@gmail.com>
|
|
3
3
|
"""URL router for the data-import plugin API."""
|
|
4
4
|
|
|
5
|
+
from django.urls import path
|
|
5
6
|
from netbox.api.routers import NetBoxRouter
|
|
6
7
|
|
|
7
8
|
from .views import (
|
|
@@ -13,6 +14,9 @@ from .views import (
|
|
|
13
14
|
ColumnTransformRuleViewSet,
|
|
14
15
|
SourceResolutionViewSet,
|
|
15
16
|
ImportExecutionViewSet,
|
|
17
|
+
InferenceBackendViewSet,
|
|
18
|
+
ResolutionProposalHistoryViewSet,
|
|
19
|
+
ResolutionProposalViewSet,
|
|
16
20
|
)
|
|
17
21
|
|
|
18
22
|
router = NetBoxRouter()
|
|
@@ -24,6 +28,15 @@ router.register("ignored-devices", IgnoredDeviceViewSet)
|
|
|
24
28
|
router.register("column-transforms", ColumnTransformRuleViewSet)
|
|
25
29
|
router.register("source-resolutions", SourceResolutionViewSet)
|
|
26
30
|
router.register("executions", ImportExecutionViewSet)
|
|
31
|
+
router.register("ai-backends", InferenceBackendViewSet)
|
|
32
|
+
router.register("resolution-proposals", ResolutionProposalViewSet)
|
|
27
33
|
|
|
28
34
|
app_name = "netbox_data_import"
|
|
29
|
-
urlpatterns =
|
|
35
|
+
urlpatterns = [
|
|
36
|
+
path(
|
|
37
|
+
"resolution-proposal-history/",
|
|
38
|
+
ResolutionProposalHistoryViewSet.as_view({"get": "list"}),
|
|
39
|
+
name="resolutionproposalhistory-list",
|
|
40
|
+
),
|
|
41
|
+
*router.urls,
|
|
42
|
+
]
|
|
@@ -3,10 +3,12 @@
|
|
|
3
3
|
"""DRF viewsets for the data-import plugin API."""
|
|
4
4
|
|
|
5
5
|
from django.http import Http404
|
|
6
|
-
from netbox.api.viewsets import NetBoxModelViewSet
|
|
7
|
-
from rest_framework import
|
|
6
|
+
from netbox.api.viewsets import NetBoxModelViewSet, NetBoxReadOnlyModelViewSet
|
|
7
|
+
from rest_framework import mixins, permissions, viewsets
|
|
8
|
+
from rest_framework.exceptions import ValidationError
|
|
8
9
|
from rest_framework.permissions import DjangoModelPermissions
|
|
9
10
|
|
|
11
|
+
from ..field_keys import SELECT_TERMINATION_TASK, parse_termination_field_key
|
|
10
12
|
from ..models import (
|
|
11
13
|
locked_profile_policy,
|
|
12
14
|
locked_resolution_policy,
|
|
@@ -18,6 +20,8 @@ from ..models import (
|
|
|
18
20
|
ColumnTransformRule,
|
|
19
21
|
SourceResolution,
|
|
20
22
|
ImportExecution,
|
|
23
|
+
InferenceBackend,
|
|
24
|
+
ResolutionProposal,
|
|
21
25
|
)
|
|
22
26
|
from .serializers import (
|
|
23
27
|
ImportProfileSerializer,
|
|
@@ -28,6 +32,9 @@ from .serializers import (
|
|
|
28
32
|
ColumnTransformRuleSerializer,
|
|
29
33
|
SourceResolutionSerializer,
|
|
30
34
|
ImportExecutionSerializer,
|
|
35
|
+
InferenceBackendSerializer,
|
|
36
|
+
ResolutionProposalHistorySerializer,
|
|
37
|
+
ResolutionProposalSerializer,
|
|
31
38
|
)
|
|
32
39
|
|
|
33
40
|
|
|
@@ -56,7 +63,23 @@ class ImportProfileViewSet(NetBoxModelViewSet):
|
|
|
56
63
|
serializer_class = ImportProfileSerializer
|
|
57
64
|
|
|
58
65
|
|
|
59
|
-
class
|
|
66
|
+
class _ProfileScopedQuerySetMixin(viewsets.GenericViewSet):
|
|
67
|
+
"""Restrict profile-owned rows and validate their optional profile filter."""
|
|
68
|
+
|
|
69
|
+
def get_queryset(self):
|
|
70
|
+
"""Return viewable rows, filtered by a valid profile ID when supplied."""
|
|
71
|
+
qs = super().get_queryset().restrict(self.request.user, "view")
|
|
72
|
+
profile_id = self.request.query_params.get("profile_id")
|
|
73
|
+
if profile_id is not None:
|
|
74
|
+
try:
|
|
75
|
+
profile_id = int(profile_id)
|
|
76
|
+
except (TypeError, ValueError) as exc:
|
|
77
|
+
raise ValidationError({"profile_id": "Enter a whole number."}) from exc
|
|
78
|
+
qs = qs.filter(profile_id=profile_id)
|
|
79
|
+
return qs
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
class _PluginModelViewSet(_ProfileScopedQuerySetMixin, viewsets.ModelViewSet):
|
|
60
83
|
"""Base class for plain-model viewsets in this plugin."""
|
|
61
84
|
|
|
62
85
|
permission_classes = [permissions.IsAuthenticated, DjangoModelPermissionsWithView]
|
|
@@ -68,14 +91,6 @@ class ColumnMappingViewSet(_PluginModelViewSet):
|
|
|
68
91
|
queryset = ColumnMapping.objects.select_related("profile")
|
|
69
92
|
serializer_class = ColumnMappingSerializer
|
|
70
93
|
|
|
71
|
-
def get_queryset(self):
|
|
72
|
-
"""Filter by profile_id query param if provided."""
|
|
73
|
-
qs = super().get_queryset()
|
|
74
|
-
profile_id = self.request.query_params.get("profile_id")
|
|
75
|
-
if profile_id:
|
|
76
|
-
qs = qs.filter(profile_id=profile_id)
|
|
77
|
-
return qs
|
|
78
|
-
|
|
79
94
|
|
|
80
95
|
class ClassRoleMappingViewSet(_PluginModelViewSet):
|
|
81
96
|
"""CRUD viewset for ClassRoleMapping."""
|
|
@@ -83,14 +98,6 @@ class ClassRoleMappingViewSet(_PluginModelViewSet):
|
|
|
83
98
|
queryset = ClassRoleMapping.objects.select_related("profile", "rack_type")
|
|
84
99
|
serializer_class = ClassRoleMappingSerializer
|
|
85
100
|
|
|
86
|
-
def get_queryset(self):
|
|
87
|
-
"""Filter by profile_id query param if provided."""
|
|
88
|
-
qs = super().get_queryset()
|
|
89
|
-
profile_id = self.request.query_params.get("profile_id")
|
|
90
|
-
if profile_id:
|
|
91
|
-
qs = qs.filter(profile_id=profile_id)
|
|
92
|
-
return qs
|
|
93
|
-
|
|
94
101
|
|
|
95
102
|
class DeviceTypeMappingViewSet(_PluginModelViewSet):
|
|
96
103
|
"""CRUD viewset for DeviceTypeMapping."""
|
|
@@ -98,14 +105,6 @@ class DeviceTypeMappingViewSet(_PluginModelViewSet):
|
|
|
98
105
|
queryset = DeviceTypeMapping.objects.select_related("profile")
|
|
99
106
|
serializer_class = DeviceTypeMappingSerializer
|
|
100
107
|
|
|
101
|
-
def get_queryset(self):
|
|
102
|
-
"""Filter by profile_id query param if provided."""
|
|
103
|
-
qs = super().get_queryset()
|
|
104
|
-
profile_id = self.request.query_params.get("profile_id")
|
|
105
|
-
if profile_id:
|
|
106
|
-
qs = qs.filter(profile_id=profile_id)
|
|
107
|
-
return qs
|
|
108
|
-
|
|
109
108
|
|
|
110
109
|
class IgnoredDeviceViewSet(_PluginModelViewSet):
|
|
111
110
|
"""CRUD viewset for IgnoredDevice."""
|
|
@@ -113,14 +112,6 @@ class IgnoredDeviceViewSet(_PluginModelViewSet):
|
|
|
113
112
|
queryset = IgnoredDevice.objects.select_related("profile")
|
|
114
113
|
serializer_class = IgnoredDeviceSerializer
|
|
115
114
|
|
|
116
|
-
def get_queryset(self):
|
|
117
|
-
"""Filter by profile_id query param if provided."""
|
|
118
|
-
qs = super().get_queryset()
|
|
119
|
-
profile_id = self.request.query_params.get("profile_id")
|
|
120
|
-
if profile_id:
|
|
121
|
-
qs = qs.filter(profile_id=profile_id)
|
|
122
|
-
return qs
|
|
123
|
-
|
|
124
115
|
|
|
125
116
|
class ColumnTransformRuleViewSet(_PluginModelViewSet):
|
|
126
117
|
"""CRUD viewset for ColumnTransformRule."""
|
|
@@ -128,14 +119,6 @@ class ColumnTransformRuleViewSet(_PluginModelViewSet):
|
|
|
128
119
|
queryset = ColumnTransformRule.objects.select_related("profile")
|
|
129
120
|
serializer_class = ColumnTransformRuleSerializer
|
|
130
121
|
|
|
131
|
-
def get_queryset(self):
|
|
132
|
-
"""Filter by profile_id query param if provided."""
|
|
133
|
-
qs = super().get_queryset()
|
|
134
|
-
profile_id = self.request.query_params.get("profile_id")
|
|
135
|
-
if profile_id:
|
|
136
|
-
qs = qs.filter(profile_id=profile_id)
|
|
137
|
-
return qs
|
|
138
|
-
|
|
139
122
|
|
|
140
123
|
def _revalidate_against_the_stored_row(serializer):
|
|
141
124
|
"""Read the resolution again and check the request against the row as it now stands.
|
|
@@ -184,26 +167,70 @@ class SourceResolutionViewSet(_PluginModelViewSet):
|
|
|
184
167
|
except (SourceResolution.DoesNotExist, ImportProfile.DoesNotExist):
|
|
185
168
|
raise Http404 from None
|
|
186
169
|
|
|
187
|
-
def get_queryset(self):
|
|
188
|
-
"""Filter by profile_id query param if provided."""
|
|
189
|
-
qs = super().get_queryset()
|
|
190
|
-
profile_id = self.request.query_params.get("profile_id")
|
|
191
|
-
if profile_id:
|
|
192
|
-
qs = qs.filter(profile_id=profile_id)
|
|
193
|
-
return qs
|
|
194
|
-
|
|
195
170
|
|
|
196
|
-
class ImportExecutionViewSet(viewsets.ReadOnlyModelViewSet):
|
|
171
|
+
class ImportExecutionViewSet(_ProfileScopedQuerySetMixin, viewsets.ReadOnlyModelViewSet):
|
|
197
172
|
"""Read-only viewset for the Import Execution audit history."""
|
|
198
173
|
|
|
199
174
|
queryset = ImportExecution.objects.select_related("profile")
|
|
200
175
|
serializer_class = ImportExecutionSerializer
|
|
201
176
|
permission_classes = [permissions.IsAuthenticated, DjangoModelPermissionsWithView]
|
|
202
177
|
|
|
178
|
+
|
|
179
|
+
class ResolutionProposalViewSet(_ProfileScopedQuerySetMixin, viewsets.ReadOnlyModelViewSet):
|
|
180
|
+
"""Read-only viewset for Resolution Proposal history."""
|
|
181
|
+
|
|
182
|
+
queryset = ResolutionProposal.objects.select_related("profile")
|
|
183
|
+
serializer_class = ResolutionProposalSerializer
|
|
184
|
+
permission_classes = [permissions.IsAuthenticated, DjangoModelPermissionsWithView]
|
|
185
|
+
|
|
203
186
|
def get_queryset(self):
|
|
204
|
-
"""
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
187
|
+
"""Return proposals only for profiles that the actor can view."""
|
|
188
|
+
profile_scope = ImportProfile.objects.restrict(self.request.user, "view")
|
|
189
|
+
return super().get_queryset().filter(profile__in=profile_scope)
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
class ResolutionProposalHistoryViewSet(mixins.ListModelMixin, viewsets.GenericViewSet):
|
|
193
|
+
"""Read one field's complete paginated history with workspace permissions."""
|
|
194
|
+
|
|
195
|
+
queryset = ResolutionProposal.objects.none()
|
|
196
|
+
serializer_class = ResolutionProposalHistorySerializer
|
|
197
|
+
permission_classes = [permissions.IsAuthenticated]
|
|
198
|
+
|
|
199
|
+
def get_queryset(self):
|
|
200
|
+
"""Return one profile field's history when the actor can view that profile."""
|
|
201
|
+
profile_values = self.request.query_params.getlist("profile_id")
|
|
202
|
+
field_values = self.request.query_params.getlist("field_key")
|
|
203
|
+
if len(profile_values) != 1 or not profile_values[0]:
|
|
204
|
+
raise ValidationError({"profile_id": "Provide one whole-number profile ID."})
|
|
205
|
+
if len(field_values) != 1 or not field_values[0]:
|
|
206
|
+
raise ValidationError({"field_key": "Provide one canonical termination field key."})
|
|
207
|
+
try:
|
|
208
|
+
profile_id = int(profile_values[0])
|
|
209
|
+
except (TypeError, ValueError) as exc:
|
|
210
|
+
raise ValidationError({"profile_id": "Provide one whole-number profile ID."}) from exc
|
|
211
|
+
try:
|
|
212
|
+
parse_termination_field_key(field_values[0])
|
|
213
|
+
except ValueError as exc:
|
|
214
|
+
raise ValidationError({"field_key": "Provide one canonical termination field key."}) from exc
|
|
215
|
+
if not ImportProfile.objects.restrict(self.request.user, "view").filter(pk=profile_id).exists():
|
|
216
|
+
raise Http404
|
|
217
|
+
return (
|
|
218
|
+
ResolutionProposal.objects.filter(
|
|
219
|
+
profile_id=profile_id,
|
|
220
|
+
task_type=SELECT_TERMINATION_TASK,
|
|
221
|
+
field_key=field_values[0],
|
|
222
|
+
)
|
|
223
|
+
.select_related("profile")
|
|
224
|
+
.order_by("-created", "-pk")
|
|
225
|
+
)
|
|
226
|
+
|
|
227
|
+
|
|
228
|
+
class InferenceBackendViewSet(NetBoxReadOnlyModelViewSet):
|
|
229
|
+
"""Read-only viewset for Inference Backend rows.
|
|
230
|
+
|
|
231
|
+
Read-only on purpose: a backend row carries the destination NetBox itself calls, and the UI form
|
|
232
|
+
is the one place that validates the `api_root` trust boundary against the allowlist.
|
|
233
|
+
"""
|
|
234
|
+
|
|
235
|
+
queryset = InferenceBackend.objects.prefetch_related("tags")
|
|
236
|
+
serializer_class = InferenceBackendSerializer
|