Skip to content

Add ability to add multiple flight plans / operational intents#77

Merged
hrishiballal merged 5 commits intomasterfrom
issue-76
Feb 23, 2026
Merged

Add ability to add multiple flight plans / operational intents#77
hrishiballal merged 5 commits intomasterfrom
issue-76

Conversation

@atti92
Copy link

@atti92 atti92 commented Feb 13, 2026

Implement #76

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds new bulk submission endpoints to allow sending multiple flight plans / operational intents to Flight Blender in a single request (Issue #76), while refactoring the existing single-item endpoints to reuse shared validation/save and intersection-processing logic.

Changes:

  • Refactors single-item set_flight_declaration / set_operational_intent into shared helper functions and a batched check_intersections implementation.
  • Adds bulk endpoints set_flight_declarations_bulk and set_operational_intents_bulk plus URL routes.
  • Introduces tests covering the refactored single-item endpoints.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 7 comments.

File Description
flight_declaration_operations/views.py Refactors validation/save flow, updates intersection checking to support batches, and adds two new bulk POST endpoints.
flight_declaration_operations/urls.py Exposes the new bulk endpoints via URL patterns.
flight_declaration_operations/tests.py Adds regression tests for the refactored single-item endpoints.
flight_declaration_operations/data_definitions.py Adds a dataclass for bulk submission responses.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@hrishiballal
Copy link

hrishiballal commented Feb 16, 2026

I will create new tests for this in the verification repo: https://github.com/openutm/verification/tree/bulk-add-declaration-operatoinal-intents

@hrishiballal
Copy link

@hrishiballal
Copy link

I have now added openutm/verification#98 for this branch, also created openutm/verification#99 to do the same for operational intents

@atti92 atti92 marked this pull request as ready for review February 19, 2026 20:17
@atti92 atti92 requested a review from Copilot February 19, 2026 20:19
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 4 out of 4 changed files in this pull request and generated 7 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

state=default_state,
)
flight_declaration.save()
flight_declaration.add_state_history_entry(new_state=0, original_state=0, notes="Created Declaration")
Copy link

Copilot AI Feb 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The state history entry is created with hardcoded new_state=0 but the declaration is saved with state=default_state. When USSP_NETWORK_ENABLED=0, default_state=1, so the declaration is created with state=1 but the history entry claims it was created with state=0. This creates an inconsistency between the actual state and the state history.

The history entry should use new_state=default_state to accurately reflect the actual state the declaration was created with.

Copilot uses AI. Check for mistakes.
Comment on lines +224 to +227
my_fd_rtree_helper = FlightDeclarationRTreeIndexFactory(index_name=FLIGHT_DECLARATION_INDEX_BASEPATH)
my_fd_rtree_helper.generate_flight_declaration_index(all_flight_declarations=declaration_list)
all_relevant_declarations = my_fd_rtree_helper.check_flight_declaration_box_intersection(view_box=view_box)
my_fd_rtree_helper.clear_rtree_index()
Copy link

Copilot AI Feb 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The FlightDeclaration RTree index is created and destroyed once per declaration in the batch (line 224-227). For a batch of N declarations, this creates O(N) RTree indices. Since the approved_ids set grows dynamically, the queryset changes for each declaration, so the index must be rebuilt.

However, this could impact performance for large batches. Consider whether the overhead of creating N indices outweighs the benefits of the optimized intersection checking. If performance becomes an issue with large batches, consider documenting a recommended maximum batch size or exploring alternative approaches.

Copilot uses AI. Check for mistakes.
Comment on lines 423 to 425
else:
if declaration_state == 0 and USSP_NETWORK_ENABLED:
if declaration_state == 0 and ussp_network_enabled:
submit_flight_declaration_to_dss_async.delay(flight_declaration_id=flight_declaration_id)
Copy link

Copilot AI Feb 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The DSS submission is only triggered if BOTH the declaration state is 0 AND ussp_network_enabled is truthy. However, if the declaration was rejected (is_approved=False), declaration_state will be 8, so this condition will correctly not trigger. But there's a subtle issue: the condition is inside the else block that only executes when NOT (all_relevant_fences AND all_relevant_declarations). This means if a declaration is approved (no conflicts), but there are no fences or declarations to check against, it will still attempt DSS submission. This appears to be intentional behavior, but the nesting could be clearer.

Copilot uses AI. Check for mistakes.
Comment on lines +515 to +567
# Phase 1: validate and save all declarations
saved: dict[int, FlightDeclaration] = {}
results: list[dict] = []
failed_count = 0

for idx, item in enumerate(flight_declarations_list):
try:
flight_declaration, error = _validate_and_save_flight_declaration(item, default_state)
if error or flight_declaration is None:
failed_count += 1
error = error or {"message": "Unknown error"}
results.append({"index": idx, "success": False, "message": error.get("message", "Validation error"), "errors": error.get("errors")})
else:
saved[idx] = flight_declaration
except Exception as e:
logger.error(f"Error validating flight declaration at index {idx}: {e}")
failed_count += 1
results.append({"index": idx, "success": False, "message": str(e)})

# Phase 2: check all intersections at once, then process results
validator = FlightDeclarationRequestValidator()
intersection_results = validator.check_intersections(list(saved.values()), ussp_network_enabled)

submitted_count = 0
for idx, flight_declaration in saved.items():
fd_id = str(flight_declaration.id)
creation_response = _process_intersection_result(
flight_declaration, intersection_results[fd_id], ussp_network_enabled
)
submitted_count += 1
results.append(
{
"index": idx,
"success": True,
"id": creation_response.id,
"is_approved": creation_response.is_approved,
"state": creation_response.state,
}
)

results.sort(key=lambda r: r["index"])

bulk_response = BulkFlightDeclarationCreateResponse(
submitted=submitted_count,
failed=failed_count,
results=results,
)
http_status = 200 if failed_count == 0 else 207
return HttpResponse(
json.dumps(asdict(bulk_response)),
status=http_status,
content_type=RESPONSE_CONTENT_TYPE,
)
Copy link

Copilot AI Feb 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The bulk endpoints lack database transaction handling. Declarations are saved individually in Phase 1 without an atomic transaction wrapper. If an exception occurs during Phase 2 (intersection checking or notification sending), the database will contain partially processed declarations with inconsistent states.

Consider wrapping the entire operation in a transaction.atomic() block, or at minimum wrap Phase 1 so that either all validations succeed and all declarations are saved, or none are saved. This would provide better consistency guarantees and simplify error recovery.

Copilot uses AI. Check for mistakes.
Comment on lines 318 to 326
is_approved=True,
start_datetime=start_datetime,
end_datetime=end_datetime,
originating_party=originating_party,
flight_declaration_raw_geojson=json.dumps(flight_declaration_geo_json),
state=declaration_state,
state=default_state,
)
flight_declaration.save()
flight_declaration.add_state_history_entry(new_state=0, original_state=0, notes="Created Declaration")
Copy link

Copilot AI Feb 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Declarations are initially saved with is_approved=True and state=default_state before intersection checking occurs. If the declaration is later rejected, _process_intersection_result updates these fields. This creates a brief window where rejected declarations appear as approved in the database.

While this is necessary for batch processing (so declarations can see each other), it could cause race conditions if other processes query for approved declarations during this window. Consider documenting this behavior or adding a transitional state to indicate "pending intersection check" to make the workflow clearer.

Copilot uses AI. Check for mistakes.
atti92 and others added 3 commits February 19, 2026 22:07
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
@hrishiballal hrishiballal merged commit e60b9bc into master Feb 23, 2026
2 of 8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants