diff --git a/backend_c/Makefile b/backend_c/Makefile index 6d23500..f34ebde 100644 --- a/backend_c/Makefile +++ b/backend_c/Makefile @@ -1,25 +1,38 @@ +# Compiler and Flags CC=gcc CFLAGS=-Iinclude -std=c99 -O2 -Wall + +# Directories and Files SRCDIR=src -OBJ=$(SRCDIR)/main.o $(SRCDIR)/fileio.o $(SRCDIR)/heap.o $(SRCDIR)/hashmap.o $(SRCDIR)/graph.o $(SRCDIR)/allocation.o $(SRCDIR)/report.o -all:build/resq -build/resq:$(OBJ)|build - $(CC) $(CFLAGS) -o $@ $(OBJ) -build: - mkdir -p build -$(SRCDIR)/main.o:$(SRCDIR)/main.c - $(CC) $(CFLAGS) -c $< -o $@ -$(SRCDIR)/fileio.o:$(SRCDIR)/fileio.c - $(CC) $(CFLAGS) -c $< -o $@ -$(SRCDIR)/heap.o:$(SRCDIR)/heap.c - $(CC) $(CFLAGS) -c $< -o $@ -$(SRCDIR)/hashmap.o:$(SRCDIR)/hashmap.c - $(CC) $(CFLAGS) -c $< -o $@ -$(SRCDIR)/graph.o:$(SRCDIR)/graph.c - $(CC) $(CFLAGS) -c $< -o $@ -$(SRCDIR)/allocation.o:$(SRCDIR)/allocation.c - $(CC) $(CFLAGS) -c $< -o $@ -$(SRCDIR)/report.o:$(SRCDIR)/report.c +BUILDDIR=build + +# --- MODIFICATION 1: Define the target executable name here --- +TARGET=$(BUILDDIR)/disaster_alloc.exe + +# List of source files +SOURCES=$(wildcard $(SRCDIR)/*.c) + +# Generate object file names from source file names +OBJECTS=$(SOURCES:$(SRCDIR)/%.c=$(BUILDDIR)/%.o) + + +# --- MODIFICATION 2: The 'all' rule now builds the correct TARGET --- +all: $(TARGET) + +# Rule to link all object files into the final executable +$(TARGET): $(OBJECTS) | $(BUILDDIR) + $(CC) $(CFLAGS) -o $@ $(OBJECTS) + +# Rule to compile a .c file into a .o file +$(BUILDDIR)/%.o: $(SRCDIR)/%.c | $(BUILDDIR) $(CC) $(CFLAGS) -c $< -o $@ + +# Rule to create the build directory if it doesn't exist +$(BUILDDIR): + mkdir -p $(BUILDDIR) + +# Rule to clean up build artifacts clean: - rm -rf build $(SRCDIR)/*.o + rm -rf $(BUILDDIR) + +.PHONY: all clean diff --git a/backend_c/build/allocation.o b/backend_c/build/allocation.o new file mode 100644 index 0000000..d4f5c77 Binary files /dev/null and b/backend_c/build/allocation.o differ diff --git a/backend_c/build/disaster_alloc.exe b/backend_c/build/disaster_alloc.exe new file mode 100644 index 0000000..53c01d8 Binary files /dev/null and b/backend_c/build/disaster_alloc.exe differ diff --git a/backend_c/build/fileio.o b/backend_c/build/fileio.o new file mode 100644 index 0000000..9725a14 Binary files /dev/null and b/backend_c/build/fileio.o differ diff --git a/backend_c/build/graph.o b/backend_c/build/graph.o new file mode 100644 index 0000000..04e2a56 Binary files /dev/null and b/backend_c/build/graph.o differ diff --git a/backend_c/build/hashmap.o b/backend_c/build/hashmap.o new file mode 100644 index 0000000..179aba6 Binary files /dev/null and b/backend_c/build/hashmap.o differ diff --git a/backend_c/build/heap.o b/backend_c/build/heap.o new file mode 100644 index 0000000..f618388 Binary files /dev/null and b/backend_c/build/heap.o differ diff --git a/backend_c/build/main.o b/backend_c/build/main.o new file mode 100644 index 0000000..556285f Binary files /dev/null and b/backend_c/build/main.o differ diff --git a/backend_c/build/report.o b/backend_c/build/report.o new file mode 100644 index 0000000..b3e0534 Binary files /dev/null and b/backend_c/build/report.o differ diff --git a/backend_c/build/resq.exe b/backend_c/build/resq.exe index d81ba3e..fbd980e 100644 Binary files a/backend_c/build/resq.exe and b/backend_c/build/resq.exe differ diff --git a/backend_c/disaster_alloc.exe b/backend_c/disaster_alloc.exe deleted file mode 100644 index d6f6819..0000000 Binary files a/backend_c/disaster_alloc.exe and /dev/null differ diff --git a/backend_c/src/allocation.c b/backend_c/src/allocation.c index bc926ce..13a940a 100644 --- a/backend_c/src/allocation.c +++ b/backend_c/src/allocation.c @@ -43,8 +43,7 @@ void run_allocator(Heap *h, Graph *g, HashMap *hm, Region *regions, int nreg, Re printf("\nStarting Resource Allocation Process...\n"); // Initialize report file at the beginning (write headers only) - init_report("../data/report.txt"); - + init_report("report.txt"); // <-- CORRECTED LINE // Count affected regions first int affected_count = 0; for (int i = 0; i < nreg; i++) { diff --git a/backend_c/src/allocation.o b/backend_c/src/allocation.o new file mode 100644 index 0000000..1caff9f Binary files /dev/null and b/backend_c/src/allocation.o differ diff --git a/backend_c/src/fileio.o b/backend_c/src/fileio.o new file mode 100644 index 0000000..9725a14 Binary files /dev/null and b/backend_c/src/fileio.o differ diff --git a/backend_c/src/graph.o b/backend_c/src/graph.o new file mode 100644 index 0000000..04e2a56 Binary files /dev/null and b/backend_c/src/graph.o differ diff --git a/backend_c/src/hashmap.o b/backend_c/src/hashmap.o new file mode 100644 index 0000000..179aba6 Binary files /dev/null and b/backend_c/src/hashmap.o differ diff --git a/backend_c/src/heap.o b/backend_c/src/heap.o new file mode 100644 index 0000000..f618388 Binary files /dev/null and b/backend_c/src/heap.o differ diff --git a/backend_c/src/main.c b/backend_c/src/main.c index 31f66d9..09efd37 100644 --- a/backend_c/src/main.c +++ b/backend_c/src/main.c @@ -9,32 +9,47 @@ #include #include +// main function now accepts command-line arguments for the input file int main(int argc, char **argv) { - const char *dataPath = "../data"; - char filePath[256]; + // --- MODIFICATION 1: Check for input file argument --- + if (argc < 2) { + fprintf(stderr, "Error: Missing input file path.\n"); + fprintf(stderr, "Usage: %s \n", argv[0]); + return 1; + } + const char *inputFilePath = argv[1]; + + // --- MODIFICATION 2: Open the input file provided by Python --- + FILE *inputFile = fopen(inputFilePath, "r"); + if (!inputFile) { + // Use perror for more descriptive file errors + perror("CRITICAL ERROR: Failed to open input file"); + return 1; + } + // This part remains the same + const char *dataPath = "../../data"; + char filePath[256]; Resource *resources = NULL; Region *regions = NULL; int nres = 0, nreg = 0; // === Load Resources === snprintf(filePath, sizeof(filePath), "%s/resources.txt", dataPath); - if (load_resources(filePath, &resources, &nres) != 0) - { - fprintf(stderr, "Error: Failed to load resources\n"); + if (load_resources(filePath, &resources, &nres) != 0) { + fprintf(stderr, "Error: Failed to load resources from %s\n", filePath); + fclose(inputFile); // Close the file before exiting return 1; } - printf("Loaded %d resources\n", nres); // === Load Regions === snprintf(filePath, sizeof(filePath), "%s/regions.txt", dataPath); - if (load_regions(filePath, ®ions, &nreg) != 0) - { - fprintf(stderr, "Error: Failed to load regions\n"); + if (load_regions(filePath, ®ions, &nreg) != 0) { + fprintf(stderr, "Error: Failed to load regions from %s\n", filePath); + fclose(inputFile); return 1; } - printf("Loaded %d regions\n", nreg); // === Setup HashMap for Resources === HashMap *hm = hm_create(2 * nres + 1); @@ -48,204 +63,94 @@ int main(int argc, char **argv) snprintf(filePath, sizeof(filePath), "%s/edges.txt", dataPath); if (load_edges(filePath, g) != 0) - fprintf(stderr, "Warning: Failed to load edges\n"); - - // === Print all regions === - printf("\n=== Disaster Resource Allocation System ===\n\n"); - printf("List of Regions:\n"); - for (int i = 0; i < nreg; i++) - printf(" [%d] %s (id: %s)\n", i + 1, regions[i].name, regions[i].id); + fprintf(stderr, "Warning: Failed to load edges from %s\n", filePath); // Default severity = 0 for all regions for (int i = 0; i < nreg; i++) regions[i].severity = 0; - // Ask number of affected regions FIRST + // --- MODIFICATION 3: Read from the input file using fscanf --- int disasterCount = 0; - printf("\nEnter number of disaster-affected regions: "); - if (scanf("%d", &disasterCount) != 1 || disasterCount < 0 || disasterCount > nreg) - { - printf("Invalid number. Exiting.\n"); - return 1; - } - while (getchar() != '\n'); // flush buffer - - int *is_disaster = calloc(nreg, sizeof(int)); - if (!is_disaster) - { + // Replace scanf("%d", &disasterCount) + if (fscanf(inputFile, "%d", &disasterCount) != 1 || disasterCount < 0 || disasterCount > nreg) { + fprintf(stderr, "Error: Invalid number of disaster regions in input file.\n"); + fclose(inputFile); return 1; } - // === Select affected regions and ONLY ask severity for affected ones === - for (int i = 0; i < disasterCount; i++) - { - int count = -1; - printf("\nSelect disaster region #%d (enter region number): ", i + 1); - if (scanf("%d", &count) != 1 || count < 1 || count > nreg) - { - printf("Invalid selection. Try again.\n"); - while (getchar() != '\n'); - i--; - continue; + // === Read affected regions and their severities from the file === + for (int i = 0; i < disasterCount; i++) { + int region_id = -1, severity = -1; + // Replace scanf("%d %d", ®ion_id, &severity) + if (fscanf(inputFile, "%d %d", ®ion_id, &severity) != 2) { + fprintf(stderr, "Error: Malformed region/severity line in input file.\n"); + fclose(inputFile); + return 1; } - while (getchar() != '\n'); // flush buffer - - int idx = count - 1; - if (is_disaster[idx]) - { - printf("Already selected. Pick another.\n"); - i--; - continue; - } - - is_disaster[idx] = 1; - // ONLY ask severity for affected regions - int sev = 0; - printf("Enter severity for %s (1-10): ", regions[idx].name); - if (scanf("%d", &sev) != 1 || sev < 1 || sev > 10) - { - printf("Invalid input. Using default severity = 1.\n"); - sev = 1; - while (getchar() != '\n'); - } - while (getchar() != '\n'); // flush buffer - regions[idx].severity = sev; - } - - // === Print safe regions === - printf("\nSafe regions:\n"); - for (int i = 0; i < nreg; i++) - if (!is_disaster[i]) - printf(" - %s (id: %s)\n", regions[i].name, regions[i].id); - - // === Resource allocation for safe regions === - printf("\nDo you want to manually enter resources for SAFE regions? (y/n): "); - char yn; - scanf("%c", &yn); - while (getchar() != '\n'); // flush buffer - - int **stock = malloc(sizeof(int *) * nres); - for (int r = 0; r < nres; r++) - stock[r] = calloc(nreg, sizeof(int)); - - if (yn == 'y' || yn == 'Y') - { - for (int i = 0; i < nreg; i++) - { - if (is_disaster[i]) continue; - printf("\n-- Safe region: %s --\n", regions[i].name); - for (int r = 0; r < nres; r++) - { - printf(" Available %s (%s): ", resources[r].name, resources[r].id); - int q = 0; - if (scanf("%d", &q) != 1 || q < 0) - { - printf("Invalid, assuming 0.\n"); - q = 0; - while (getchar() != '\n'); - } - stock[r][i] = q; + // Find the region index from its ID. Note: region IDs in your app start from 1. + int region_idx = -1; + for (int j = 0; j < nreg; j++) { + // Assuming region IDs are strings, so we convert the read int to string to compare + char id_str[10]; + snprintf(id_str, 10, "%d", region_id); + if (strcmp(regions[j].id, id_str) == 0) { + region_idx = j; + break; } } - } - else - { - int safeCount = 0; - for (int i = 0; i < nreg; i++) - if (!is_disaster[i]) safeCount++; - if (safeCount == 0) safeCount = 1; - - for (int r = 0; r < nres; r++) - { - int per = resources[r].quantity / safeCount; - int rem = resources[r].quantity % safeCount; - for (int i = 0; i < nreg; i++) - { - if (is_disaster[i]) { stock[r][i] = 0; continue; } - stock[r][i] = per + (rem > 0 ? 1 : 0); - if (rem > 0) rem--; - } + + if (region_idx == -1) { + fprintf(stderr, "Error: Region with ID %d not found.\n", region_id); + continue; // Skip this invalid region } - printf("\nDistributed global resources to safe regions.\n"); - } - // === Save disaster_config.txt === - snprintf(filePath, sizeof(filePath), "%s/disaster_config.txt", dataPath); - FILE *df = fopen(filePath, "w"); - if (df) - { - fprintf(df, "#region_index,region_id,region_name,severity,population\n"); - for (int i = 0; i < nreg; i++) - if (is_disaster[i]) - fprintf(df, "%d,%s,%s,%d,%d\n", i, regions[i].id, regions[i].name, regions[i].severity, regions[i].population); - fclose(df); - printf("Saved disaster_config.txt\n"); + if (severity < 1 || severity > 10) { + fprintf(stderr, "Warning: Invalid severity %d for region %d. Defaulting to 1.\n", severity, region_id); + severity = 1; + } + regions[region_idx].severity = severity; } - // === Save region_resources.txt === - snprintf(filePath, sizeof(filePath), "%s/region_resources.txt", dataPath); - FILE *rf = fopen(filePath, "w"); - if (rf) - { - fprintf(rf, "#region_index,region_id,resource_id,resource_name,quantity\n"); - for (int i = 0; i < nreg; i++) - for (int r = 0; r < nres; r++) - if (stock[r][i] > 0) - fprintf(rf, "%d,%s,%s,%s,%d\n", i, regions[i].id, resources[r].id, resources[r].name, stock[r][i]); - fclose(rf); - printf("Saved region_resources.txt\n"); - } + // --- MODIFICATION 4: Close the input file --- + fclose(inputFile); // === Prepare Heap of Disaster Requests === - // Create a request for EACH resource in each affected region Heap *h = heap_create(disasterCount * nres + 5); int req_counter = 0; - - for (int i = 0; i < nreg; i++) - { - if (!is_disaster[i]) continue; - - // For each resource, create a request - for (int r = 0; r < nres; r++) - { - Request *req = malloc(sizeof(Request)); - snprintf(req->id, IDLEN, "RQ_%d", req_counter++); - strncpy(req->region_id, regions[i].id, IDLEN - 1); - req->region_id[IDLEN - 1] = '\0'; - strncpy(req->resource_id, resources[r].id, IDLEN - 1); // ASSIGN RESOURCE ID - req->resource_id[IDLEN - 1] = '\0'; - - printf("DEBUG: Request %s - Region: %s, Resource: %s\n", req->id, req->region_id, req->resource_id); - - req->qty_needed = regions[i].population; - - int priority = regions[i].severity * 1000 + regions[i].population; - heap_insert(h, req, priority); + for (int i = 0; i < nreg; i++) { + if (regions[i].severity > 0) { // Check if it's a disaster region + for (int r = 0; r < nres; r++) { + Request *req = malloc(sizeof(Request)); + snprintf(req->id, IDLEN, "RQ_%d", req_counter++); + strncpy(req->region_id, regions[i].id, IDLEN - 1); + req->region_id[IDLEN - 1] = '\0'; + strncpy(req->resource_id, resources[r].id, IDLEN - 1); + req->resource_id[IDLEN - 1] = '\0'; + + // Simplified logic: request quantity based on severity + req->qty_needed = regions[i].severity * 100; + + int priority = regions[i].severity * 1000 + regions[i].population; + heap_insert(h, req, priority); + } } } - - // === Generate Report === - snprintf(filePath, sizeof(filePath), "%s/report.txt", dataPath); - - // === Allocation Process === + + // === Allocation Process and Report Generation === + // This part assumes run_allocator does the main work and generates the report run_allocator(h, g, hm, regions, nreg, resources, nres); - // === Cleanup Requests in Heap === + + // === Cleanup === Request *r; while ((r = heap_pop(h)) != NULL) free(r); heap_free(h); - - // === Cleanup Other Resources === hm_free(hm); graph_free(g); - for (int r = 0; r < nres; r++) free(stock[r]); - free(stock); - free(is_disaster); free(resources); free(regions); - printf("\nSetup complete. Allocation finished (see report.txt).\n"); - return 0; -} \ No newline at end of file + return 0; // SUCCESS! +} diff --git a/backend_c/src/main.o b/backend_c/src/main.o new file mode 100644 index 0000000..4300bc0 Binary files /dev/null and b/backend_c/src/main.o differ diff --git a/backend_c/src/report.o b/backend_c/src/report.o new file mode 100644 index 0000000..b3e0534 Binary files /dev/null and b/backend_c/src/report.o differ diff --git a/data/input.txt b/data/input.txt new file mode 100644 index 0000000..bf12f05 --- /dev/null +++ b/data/input.txt @@ -0,0 +1,3 @@ +2 +6 6 +7 10 diff --git a/data/regions.txt b/data/regions.txt index 0e1884f..a66969c 100644 --- a/data/regions.txt +++ b/data/regions.txt @@ -1,14 +1,14 @@ #ID,Name,Severity(optional),Population -RG1,Dehradun,0,1696694 -RG2,Haridwar,0,1890422 -RG3,Tehri Garhwal,0,618931 -RG4,Pauri Garhwal,0,687271 -RG5,Chamoli,0,391605 -RG6,Nainital,0,954605 -RG7,Almora,0,622506 -RG8,Bageshwar,0,259898 -RG9,Champawat,0,259648 -RG10,Pithoragarh,0,483439 -RG11,Rudraprayag,0,242285 -RG12,Udham Singh Nagar,0,1648902 -RG13,Uttarkashi,0,330086 +1,Dehradun,0,1696694 +2,Haridwar,0,1890422 +3,Tehri Garhwal,0,618931 +4,Pauri Garhwal,0,687271 +5,Chamoli,0,391605 +6,Nainital,0,954605 +7,Almora,0,622506 +8,Bageshwar,0,259898 +9,Champawat,0,259648 +10,Pithoragarh,0,483439 +11,Rudraprayag,0,242285 +12,Udham Singh Nagar,0,1648902 +13,Uttarkashi,0,330086 diff --git a/frontend_python/app.py b/frontend_python/app.py index e69de29..fe37876 100644 --- a/frontend_python/app.py +++ b/frontend_python/app.py @@ -0,0 +1,252 @@ +from flask import Flask, render_template, request, jsonify +import subprocess +import os +import uuid +from pathlib import Path + +app = Flask(__name__) + +# Calculate absolute paths +FRONTEND_DIR = Path(__file__).parent.absolute() +PROJECT_ROOT = FRONTEND_DIR.parent +BACKEND_BUILD_DIR = PROJECT_ROOT / 'backend_c' / 'build' +DISASTER_ALLOC_EXE = BACKEND_BUILD_DIR / 'disaster_alloc.exe' + +@app.route('/') +def index(): + """Serve the main dashboard page""" + return render_template('index.html') + +@app.route('/allocate', methods=['POST']) +def allocate(): + """ + Handle allocation requests from the frontend + Expected JSON body: {'data': 'user input from textarea'} + """ + temp_input_file = None + report_file = None + + try: + data = request.get_json() + + if not data or 'data' not in data: + return jsonify({ + 'status': 'error', + 'message': 'No data provided' + }), 400 + + user_input = data.get('data', '').strip() + + if not user_input: + return jsonify({ + 'status': 'error', + 'message': 'Input data is empty' + }), 400 + + # Create temporary input file with unique name in frontend directory + temp_filename = f'temp_input_{uuid.uuid4().hex}.txt' + temp_input_file = FRONTEND_DIR / temp_filename + + with open(temp_input_file, 'w') as f: + f.write(user_input) + + # Calculate relative path from backend_c/build/ to the temp file + # This will be something like: ../../frontend_python/temp_input_XXX.txt + relative_input_path = os.path.relpath(temp_input_file, BACKEND_BUILD_DIR) + + # Run the C executable from backend_c/build/ directory + try: + result = subprocess.run( + [str(DISASTER_ALLOC_EXE), relative_input_path], + cwd=str(BACKEND_BUILD_DIR), + capture_output=True, + text=True, + timeout=30 + ) + + # Check for errors + if result.returncode != 0: + return jsonify({ + 'status': 'error', + 'message': f'Backend process failed with return code {result.returncode}', + 'stderr': result.stderr, + 'stdout': result.stdout + }), 500 + + # Read the report file generated by C program in backend_c/build/ + report_file = BACKEND_BUILD_DIR / 'report.txt' + + if not report_file.exists(): + return jsonify({ + 'status': 'error', + 'message': 'Report file not generated by backend', + 'stdout': result.stdout, + 'stderr': result.stderr + }), 500 + + with open(report_file, 'r') as f: + report_content = f.read() + + return jsonify({ + 'status': 'success', + 'report': report_content + }) + + except subprocess.TimeoutExpired: + return jsonify({ + 'status': 'error', + 'message': 'Backend process timed out after 30 seconds' + }), 500 + except FileNotFoundError: + return jsonify({ + 'status': 'error', + 'message': f'Executable not found at: {DISASTER_ALLOC_EXE}' + }), 500 + + except Exception as e: + return jsonify({ + 'status': 'error', + 'message': f'Unexpected error: {str(e)}' + }), 500 + + finally: + # Cleanup: Delete temporary files + if temp_input_file and temp_input_file.exists(): + try: + os.remove(temp_input_file) + except Exception: + pass + + if report_file and report_file.exists(): + try: + os.remove(report_file) + except Exception: + pass + +@app.route('/run_with_input', methods=['POST']) +def run_with_input(): + """ + Alternative endpoint for structured input (for dashboard UI) + Expected JSON body: + { + "regions": [1, 4, 7], + "severities": [5, 8, 3], + "manualResourceEntry": false, + "manualStock": {} + } + """ + temp_input_file = None + report_file = None + + try: + data = request.get_json() + + if not data: + return jsonify({ + 'status': 'error', + 'message': 'No data provided' + }), 400 + + regions = data.get('regions', []) + severities = data.get('severities', []) + + if not regions or not severities: + return jsonify({ + 'status': 'error', + 'message': 'Regions and severities are required' + }), 400 + + if len(regions) != len(severities): + return jsonify({ + 'status': 'error', + 'message': 'Regions and severities must have the same length' + }), 400 + + # Create temporary input file with unique name + temp_filename = f'temp_input_{uuid.uuid4().hex}.txt' + temp_input_file = FRONTEND_DIR / temp_filename + + with open(temp_input_file, 'w') as f: + f.write(f"{len(regions)}\n") + for region_id, severity in zip(regions, severities): + f.write(f"{region_id} {severity}\n") + + # Calculate relative path from backend_c/build/ to the temp file + relative_input_path = os.path.relpath(temp_input_file, BACKEND_BUILD_DIR) + + # Run the C executable from backend_c/build/ directory + try: + result = subprocess.run( + [str(DISASTER_ALLOC_EXE), relative_input_path], + cwd=str(BACKEND_BUILD_DIR), + capture_output=True, + text=True, + timeout=30 + ) + + if result.returncode != 0: + return jsonify({ + 'status': 'error', + 'message': f'Backend process failed with return code {result.returncode}', + 'stderr': result.stderr, + 'stdout': result.stdout + }), 500 + + # Read the report file + report_file = BACKEND_BUILD_DIR / 'report.txt' + + if not report_file.exists(): + return jsonify({ + 'status': 'error', + 'message': 'Report file not generated', + 'stdout': result.stdout, + 'stderr': result.stderr + }), 500 + + with open(report_file, 'r') as f: + report_content = f.read() + + return jsonify({ + 'status': 'success', + 'report': report_content + }) + + except subprocess.TimeoutExpired: + return jsonify({ + 'status': 'error', + 'message': 'Backend process timed out' + }), 500 + except FileNotFoundError: + return jsonify({ + 'status': 'error', + 'message': f'Executable not found at: {DISASTER_ALLOC_EXE}' + }), 500 + + except Exception as e: + return jsonify({ + 'status': 'error', + 'message': str(e) + }), 500 + + finally: + # Cleanup + if temp_input_file and temp_input_file.exists(): + try: + os.remove(temp_input_file) + except Exception: + pass + + if report_file and report_file.exists(): + try: + os.remove(report_file) + except Exception: + pass + +if __name__ == '__main__': + # Verify paths on startup + print(f"Frontend Directory: {FRONTEND_DIR}") + print(f"Backend Build Directory: {BACKEND_BUILD_DIR}") + print(f"Executable Path: {DISASTER_ALLOC_EXE}") + print(f"Executable exists: {DISASTER_ALLOC_EXE.exists()}") + + app.run(debug=True, port=5000) \ No newline at end of file diff --git a/frontend_python/requirements.txt b/frontend_python/requirements.txt index e69de29..43c008e 100644 --- a/frontend_python/requirements.txt +++ b/frontend_python/requirements.txt @@ -0,0 +1,3 @@ + +Flask==3.0.0 +Werkzeug==3.0.1 \ No newline at end of file diff --git a/frontend_python/static/script.js b/frontend_python/static/script.js new file mode 100644 index 0000000..068b297 --- /dev/null +++ b/frontend_python/static/script.js @@ -0,0 +1,331 @@ +// Region data with coordinates for Uttarakhand +const regions = [ + { id: 1, name: 'Dehradun', lat: 30.3165, lng: 78.0322, color: '#3b82f6' }, + { id: 2, name: 'Haridwar', lat: 29.9457, lng: 78.1642, color: '#10b981' }, + { id: 3, name: 'Tehri', lat: 30.3909, lng: 78.4800, color: '#f59e0b' }, + { id: 4, name: 'Pauri', lat: 30.1535, lng: 78.7757, color: '#ef4444' }, + { id: 5, name: 'Chamoli', lat: 30.4000, lng: 79.3300, color: '#8b5cf6' }, + { id: 6, name: 'Nainital', lat: 29.3803, lng: 79.4636, color: '#ec4899' }, + { id: 7, name: 'Almora', lat: 29.5971, lng: 79.6591, color: '#06b6d4' } +]; + +let map; +let markers = {}; +let regionStates = {}; + +// Initialize the application +document.addEventListener('DOMContentLoaded', () => { + initializeMap(); + renderRegions(); + attachEventListeners(); +}); + +// Initialize Leaflet map +function initializeMap() { + map = L.map('map').setView([30.0668, 79.0193], 8); + + L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', { + attribution: '© OpenStreetMap contributors' + }).addTo(map); + + // Add markers for each region + regions.forEach(region => { + const marker = L.circleMarker([region.lat, region.lng], { + radius: 8, + fillColor: '#cbd5e1', + color: '#64748b', + weight: 2, + opacity: 1, + fillOpacity: 0.6 + }).addTo(map); + + marker.bindPopup(`${region.name}
Click to toggle`); + marker.on('click', () => toggleRegion(region.id)); + markers[region.id] = marker; + }); +} + +// Render region cards +function renderRegions() { + const regionList = document.getElementById('region-list'); + + regions.forEach(region => { + regionStates[region.id] = { selected: false, severity: 5 }; + + const card = document.createElement('div'); + card.className = 'region-card bg-gray-50 rounded-lg p-4 border-2 border-gray-200'; + card.id = `region-${region.id}`; + + card.innerHTML = ` +
+
+ + ${region.name} +
+ + Level ${5} + +
+
+ Severity: + + 5 +
+
+
+
+ `; + + regionList.appendChild(card); + }); +} + +// Attach event listeners +function attachEventListeners() { + // Region checkboxes + document.querySelectorAll('.region-checkbox').forEach(checkbox => { + checkbox.addEventListener('change', (e) => { + const regionId = parseInt(e.target.dataset.regionId); + toggleRegion(regionId); + }); + }); + + // Severity sliders + document.querySelectorAll('.severity-slider').forEach(slider => { + slider.addEventListener('input', (e) => { + const regionId = parseInt(e.target.dataset.regionId); + const severity = parseInt(e.target.value); + updateSeverity(regionId, severity); + }); + }); + + // Control buttons + document.getElementById('select-all-btn').addEventListener('click', selectAll); + document.getElementById('clear-all-btn').addEventListener('click', clearAll); + document.getElementById('run-allocation-btn').addEventListener('click', runAllocation); +} + +// Toggle region selection +function toggleRegion(regionId) { + const card = document.getElementById(`region-${regionId}`); + const checkbox = card.querySelector('.region-checkbox'); + const slider = card.querySelector('.severity-slider'); + + regionStates[regionId].selected = !regionStates[regionId].selected; + checkbox.checked = regionStates[regionId].selected; + slider.disabled = !regionStates[regionId].selected; + + if (regionStates[regionId].selected) { + card.classList.remove('bg-gray-50', 'border-gray-200'); + card.classList.add('bg-blue-50', 'border-blue-400'); + + const region = regions.find(r => r.id === regionId); + markers[regionId].setStyle({ + fillColor: region.color, + color: region.color, + fillOpacity: 0.8 + }); + } else { + card.classList.remove('bg-blue-50', 'border-blue-400'); + card.classList.add('bg-gray-50', 'border-gray-200'); + + markers[regionId].setStyle({ + fillColor: '#cbd5e1', + color: '#64748b', + fillOpacity: 0.6 + }); + } +} + +// Update severity value +function updateSeverity(regionId, severity) { + regionStates[regionId].severity = severity; + + const card = document.getElementById(`region-${regionId}`); + card.querySelector('.severity-value').textContent = severity; + card.querySelector('.severity-display').textContent = severity; + + const color = getSeverityColor(severity); + card.querySelector('.severity-badge').style.backgroundColor = color; + card.querySelector('.progress-bar').style.width = `${severity * 10}%`; + card.querySelector('.progress-bar').style.backgroundColor = color; +} + +// Get severity color +function getSeverityColor(severity) { + if (severity <= 3) return '#10b981'; // Green + if (severity <= 6) return '#fbbf24'; // Yellow + if (severity <= 8) return '#f97316'; // Orange + return '#ef4444'; // Red +} + +// Select all regions +function selectAll() { + regions.forEach(region => { + if (!regionStates[region.id].selected) { + toggleRegion(region.id); + } + }); +} + +// Clear all selections +function clearAll() { + regions.forEach(region => { + if (regionStates[region.id].selected) { + toggleRegion(region.id); + } + }); +} + +// Run allocation +async function runAllocation() { + const selectedRegions = regions + .filter(r => regionStates[r.id].selected) + .map(r => r.id); + + const severities = selectedRegions.map(id => regionStates[id].severity); + + if (selectedRegions.length === 0) { + showToast('Please select at least one region', 'error'); + return; + } + + const payload = { + regions: selectedRegions, + severities: severities, + manualResourceEntry: false, + manualStock: {} + }; + + console.log('Sending payload:', payload); + + // Show loading state + document.getElementById('empty-state').classList.add('hidden'); + document.getElementById('results-container').classList.add('hidden'); + document.getElementById('loading-state').classList.remove('hidden'); + document.getElementById('run-allocation-btn').disabled = true; + + try { + const response = await fetch('/run_with_input', { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify(payload) + }); + + const data = await response.json(); + + if (data.status === 'success') { + displayResults(data.report); + showToast('Allocation completed successfully', 'success'); + } else { + throw new Error(data.message || 'Allocation failed'); + } + } catch (error) { + console.error('Error:', error); + showToast(`Error: ${error.message}`, 'error'); + document.getElementById('loading-state').classList.add('hidden'); + document.getElementById('empty-state').classList.remove('hidden'); + } finally { + document.getElementById('run-allocation-btn').disabled = false; + } +} + +// Display results +function displayResults(report) { + document.getElementById('loading-state').classList.add('hidden'); + document.getElementById('results-container').classList.remove('hidden'); + document.getElementById('summary-section').classList.remove('hidden'); + + const lines = report.split('\n').filter(line => line.trim() !== ''); + const tbody = document.getElementById('results-tbody'); + tbody.innerHTML = ''; + + let totalRequests = 0; + let totalAllocated = 0; + let totalCost = 0; + + // Skip header line + const dataLines = lines.slice(1); + + dataLines.forEach((line, index) => { + const parts = line.split(',').map(p => p.trim()); + + if (parts.length >= 8) { + const [reqId, regionId, resourceId, allocated, route, cost, status, timestamp] = parts; + + totalRequests++; + totalAllocated += parseInt(allocated) || 0; + totalCost += parseFloat(cost) || 0; + + const row = document.createElement('tr'); + row.className = 'hover:bg-gray-50 animate-slide-in'; + row.style.animationDelay = `${index * 0.05}s`; + + const regionName = regions.find(r => r.id === parseInt(regionId))?.name || regionId; + + row.innerHTML = ` + ${reqId} + ${regionName} + ${resourceId} + ${allocated} + ${route} + ₹${parseFloat(cost).toFixed(2)} + + ${status} + + ${timestamp} + `; + + tbody.appendChild(row); + } + }); + + // Update summary + document.getElementById('total-requests').textContent = totalRequests; + document.getElementById('total-allocated').textContent = totalAllocated; + document.getElementById('total-cost').textContent = `₹${totalCost.toFixed(2)}`; +} + +// Get status badge class +function getStatusClass(status) { + const statusUpper = status.toUpperCase(); + if (statusUpper.includes('FULL')) { + return 'bg-green-100 text-green-800'; + } else if (statusUpper.includes('PARTIAL')) { + return 'bg-yellow-100 text-yellow-800'; + } else if (statusUpper.includes('OUT_OF_STOCK') || statusUpper.includes('FAILED')) { + return 'bg-red-100 text-red-800'; + } else { + return 'bg-gray-100 text-gray-800'; + } +} + +// Show toast notification +function showToast(message, type = 'info') { + const toast = document.createElement('div'); + toast.className = `toast ${type === 'success' ? 'bg-green-500' : type === 'error' ? 'bg-red-500' : 'bg-blue-500'} text-white`; + + const icon = type === 'success' ? 'check-circle' : type === 'error' ? 'exclamation-circle' : 'info-circle'; + + toast.innerHTML = ` +
+ + ${message} +
+ `; + + document.body.appendChild(toast); + + setTimeout(() => { + toast.style.opacity = '0'; + setTimeout(() => toast.remove(), 300); + }, 3000); +} \ No newline at end of file diff --git a/frontend_python/templates/index.html b/frontend_python/templates/index.html new file mode 100644 index 0000000..7bdf4f0 --- /dev/null +++ b/frontend_python/templates/index.html @@ -0,0 +1,195 @@ + + + + + + Disaster Relief Resource Allocation System + + + + + + + + + + + +
+
+ +
+
+

+ + Region Configuration +

+ + +
+
+
+ + +
+ +
+ + +
+ + + +
+
+
+ + +
+ + + + +
+

+ + Allocation Report +

+ + + + + +
+ +

Select regions and run allocation to view results

+
+ + + +
+
+
+
+ + + + \ No newline at end of file diff --git a/frontend_python/test_input.txt b/frontend_python/test_input.txt new file mode 100644 index 0000000..0092d70 --- /dev/null +++ b/frontend_python/test_input.txt @@ -0,0 +1,3 @@ +2 +1 8 +4 5 \ No newline at end of file