diff --git a/.installation_state/django_migrations.done b/.installation_state/django_migrations.done new file mode 100644 index 00000000..3a907cb4 --- /dev/null +++ b/.installation_state/django_migrations.done @@ -0,0 +1 @@ +2026-06-07T07:47:07Z diff --git a/.installation_state/env_file.done b/.installation_state/env_file.done new file mode 100644 index 00000000..7812f049 --- /dev/null +++ b/.installation_state/env_file.done @@ -0,0 +1 @@ +2026-06-07T07:36:50Z diff --git a/.installation_state/superuser.done b/.installation_state/superuser.done new file mode 100644 index 00000000..e04b301c --- /dev/null +++ b/.installation_state/superuser.done @@ -0,0 +1 @@ +2026-06-07T07:49:33Z diff --git a/computing/api.py b/computing/api.py index 7abeb36c..f30348f0 100644 --- a/computing/api.py +++ b/computing/api.py @@ -69,7 +69,12 @@ from .surface_water_bodies.merge_swb_ponds import merge_swb_ponds from utilities.auth_check_decorator import api_security_check from computing.layer_dependency.layer_generation_in_order import layer_generate_map -from .views import layer_status, get_layers_of_workspace, check_missing_layers +from .views import ( + layer_status, + get_layers_of_workspace, + missing_layer_for_all_workspace, + clear_layer_cache, +) from .misc.lcw_conflict import generate_lcw_conflict_data from .misc.agroecological_space import generate_agroecological_data from .misc.factory_csr import generate_factory_csr_data @@ -1877,14 +1882,20 @@ def sync_layer_remote(request): @schema(None) def missing_layers(request): try: - workspace = request.query_params.get("workspace").lower() - result = check_missing_layers(workspace) + result = missing_layer_for_all_workspace() return Response({"result": result}, status=status.HTTP_200_OK) except Exception as e: print("Exception in get_layers_for_workspace api :: ", e) return Response({"Exception": e}, status=status.HTTP_500_INTERNAL_SERVER_ERROR) +@api_view(["GET"]) +@schema(None) +def refresh_layer_cache(request, workspace=None): + clear_layer_cache(workspace) + return Response({"message": f"Cache cleared for: {workspace or 'all workspaces'}"}) + + @api_view(["POST"]) @schema(None) def generate_fabdem_layer(request): diff --git a/computing/lulc_X_terrain/lulc_on_plain_cluster.py b/computing/lulc_X_terrain/lulc_on_plain_cluster.py index 00c073ed..1f2d04e6 100644 --- a/computing/lulc_X_terrain/lulc_on_plain_cluster.py +++ b/computing/lulc_X_terrain/lulc_on_plain_cluster.py @@ -1,304 +1,304 @@ -import ee -from nrm_app.celery import app -from computing.utils import ( - sync_layer_to_geoserver, - save_layer_info_to_db, - update_layer_sync_status, - create_chunk, - merge_chunks, -) -from utilities.gee_utils import ( - ee_initialize, - check_task_status, - valid_gee_text, - get_gee_asset_path, - is_gee_asset_exists, - export_vector_asset_to_gee, - make_asset_public, - get_gee_dir_path, -) -from .utils import aez_lulcXterrain_cluster_centroids, process_mws, calculate_area -from utilities.constants import AEZ, GEE_HELPER_PATH - - -@app.task(bind=True) -def lulc_on_plain_cluster( - self, state, district, block, start_year, end_year, gee_account_id -): - ee_initialize(gee_account_id) - - asset_description = ( - valid_gee_text(district.lower()) - + "_" - + valid_gee_text(block.lower()) - + "_lulcXplains_clusters_bk02_june" - ) - asset_id = get_gee_asset_path(state, district, block) + asset_description - - if not is_gee_asset_exists(asset_id): - aez_india = ee.FeatureCollection(AEZ) - - landforms = ee.Image( - get_gee_asset_path(state, district, block) - + "terrain_raster_" - + valid_gee_text(district.lower()) - + "_" - + valid_gee_text(block.lower()) - ) # The eleven landforms raster - - mwsheds = ee.FeatureCollection( - get_gee_asset_path(state, district, block) - + "filtered_mws_" - + valid_gee_text(district.lower()) - + "_" - + valid_gee_text(block.lower()) - + "_uid" - ) - - filtered_aez = aez_india.filterBounds(mwsheds.geometry()) - - aez_no = filtered_aez.first().get("ae_regcode").getInfo() - - lulc_imgs = [] - for y in range(start_year, end_year + 1): - lulc_img = ee.Image( - get_gee_asset_path(state, district, block) - + valid_gee_text(district.lower()) - + "_" - + valid_gee_text(block.lower()) - + "_" - + str(y) - + "-07-01_" - + str(y + 1) - + "-06-30_LULCmap_10m" - ) - lulc_imgs.append(lulc_img) - - lulc_img_collection = ee.ImageCollection.fromImages(lulc_imgs) - study_area_lulc = lulc_img_collection.mode().clip(mwsheds) - study_area_landforms = landforms.clip(mwsheds) - - mwsheds_with_clusters = process_mws(mwsheds) - plain_mwsheds = mwsheds_with_clusters.filter( - ee.Filter.neq("terrain_cluster", 2) - ) - plain_centroids = aez_lulcXterrain_cluster_centroids[f"aez{aez_no}"]["plains"] - - chunk_size = 50 - rois, descs = create_chunk(mwsheds, asset_description, chunk_size) - - - tasks = [] - temp_assets = [] - for roi, desc in zip(rois, descs): - chunk_with_clusters = process_mws(roi) - plain_chunk = chunk_with_clusters.filter( - ee.Filter.neq("terrain_cluster", 2) - ) - - - result_chunk = process_feature_collection( - plain_chunk, study_area_landforms, study_area_lulc, plain_centroids - ) - - chunk_asset_id = get_gee_dir_path([state, district, block], GEE_HELPER_PATH) + desc - temp_assets.append(chunk_asset_id) - - - task = export_vector_asset_to_gee( - result_chunk, desc, chunk_asset_id - ) - if task: - tasks.append(task) - - - print("Started all chunk tasks") - task_id_list = check_task_status(tasks) - print("All chunk tasks completed:", task_id_list) - - - # Merge all chunks into one feature collection - print("Starting merge task") - final_task_id = merge_chunks( - mwsheds, - [state, district, block], - asset_description, - chunk_size, - merge_asset_id=asset_id, - ) - if final_task_id: - final_task_status = check_task_status([final_task_id]) - print("Final merge task completed:", final_task_status) - - - # Clean up temporary assets - for chunk_id in temp_assets: - if is_gee_asset_exists(chunk_id): - try: - ee.data.deleteAsset(chunk_id) - print(f"Deleted temp asset {chunk_id}") - except Exception as e: - print(f"Failed to delete {chunk_id}: {e}") - - - layer_at_geoserver = False - if is_gee_asset_exists(asset_id): - layer_id = save_layer_info_to_db( - state, - district, - block, - layer_name=f"{valid_gee_text(district.lower())}_{valid_gee_text(block.lower())}_lulc_plain", - asset_id=asset_id, - dataset_name="Terrain LULC", - misc={ - "start_year": start_year, - "end_year": end_year, - }, - ) - make_asset_public(asset_id) - - fc = ee.FeatureCollection(asset_id).getInfo() - fc = {"features": fc["features"], "type": fc["type"]} - res = sync_layer_to_geoserver( - state, - fc, - valid_gee_text(district.lower()) - + "_" - + valid_gee_text(block.lower()) - + "_lulc_plain", - "terrain_lulc", - ) - print(res) - if res["status_code"] == 201 and layer_id: - update_layer_sync_status(layer_id=layer_id, sync_to_geoserver=True) - print("sync to geoserver flag updated") - layer_at_geoserver = True - return layer_at_geoserver - - -def process_feature_collection(fc, landforms, area_lulc, plain_centroids): - """ - Process an entire FeatureCollection by applying the L2 cluster assignment. - """ - return fc.map(lambda f: assign_l2_cluster(f, landforms, area_lulc, plain_centroids)) - - -def assign_l2_cluster(feature, landforms, area_lulc, plain_centroids): - """ - Assigns L2 clusters to features based on landform and land use characteristics. - """ - study_area = feature.geometry() - lf300x2k = landforms.clip(study_area) - - # Get LULC data - lulc = area_lulc.select("predicted_label") - - # # Convert 10 landforms to 4 general landforms - # slopy = lf300x2k.eq(6) - # plains = lf300x2k.eq(5).Or(lf300x2k.gte(12)) - # steep_slopes = lf300x2k.eq(8) - # ridge = lf300x2k.eq(7).Or(lf300x2k.gte(9).And(lf300x2k.lte(11))) - # valleys = lf300x2k.gte(1).And(lf300x2k.lte(4)) - - # Convert 10 landforms to 4 general landforms - slopy = lf300x2k.eq(6) - plains = lf300x2k.eq(5) - steep_slopes = lf300x2k.eq(8) - # ridge = lf300x2k.gte(9).Or(lf300x2k.eq(7)) - # valleys = lf300x2k.gte(1).And(lf300x2k.lte(4)) - ridge = lf300x2k.eq(3).Or(lf300x2k.eq(7)).Or(lf300x2k.eq(10)).Or(lf300x2k.eq(11)) - valleys = lf300x2k.eq(1).Or(lf300x2k.eq(2)).Or(lf300x2k.eq(4)).Or(lf300x2k.eq(9)) - - # Calculate areas - plain_area = calculate_area(plains, study_area) - valley_area = calculate_area(valleys, study_area) - hill_slopes_area = calculate_area(steep_slopes, study_area) - slopy_area = calculate_area(slopy, study_area) - - plain_plus_slope_area = plain_area.add(slopy_area) - - # Calculate LULC proportions - def calculate_lulc_proportion(lulc_class): - area_image = ( - plains.eq(1) - .And(lulc.eq(lulc_class)) - .multiply(ee.Image.pixelArea()) - .rename("area") - ) - - area = area_image.reduceRegion( - reducer=ee.Reducer.sum(), geometry=study_area, scale=30, maxPixels=1e10 - ) - - return ee.Number(area.get("area")).divide(1e6).divide(plain_plus_slope_area) - - # Calculate all proportions - plains_barren = calculate_lulc_proportion(7) # Barren - plains_double_crop = calculate_lulc_proportion(10) # Double crop - plains_shrubs_scrubs = calculate_lulc_proportion(12) # Shrubs/scrubs - plains_single_crop = calculate_lulc_proportion(8) # Single crop - plains_single_non_kharif_crop = calculate_lulc_proportion(9) # Single non-kharif - plains_forest = calculate_lulc_proportion(6) # Forest - plains_triple_crop = calculate_lulc_proportion(11) # Triple crop - - # Create feature vector - plain_new_feature_vector = ee.List( - [ - plains_barren, - plains_double_crop, - plains_shrubs_scrubs, - plains_single_crop, - plains_single_non_kharif_crop, - plains_forest, - plains_triple_crop, - ] - ) - - # Convert centroids to ee.List format - centroid_vectors = [ - plain_centroids[str(i)]["cluster_vector"] for i in range(len(plain_centroids)) - ] - ee_centroid_vectors = ee.List(centroid_vectors) - - # Calculate distances - def diff_func(value_pair): - return ( - ee.Number(ee.List(value_pair).get(0)) - .subtract(ee.Number(ee.List(value_pair).get(1))) - .pow(2) - ) - - def calculate_distances(centroid): - centroid_list = ee.List(centroid) - paired_values = centroid_list.zip(plain_new_feature_vector) - return paired_values.map(diff_func).reduce(ee.Reducer.sum()) - - distances_plain = ee_centroid_vectors.map(calculate_distances) - - # Find closest cluster - min_distance_plain = distances_plain.reduce(ee.Reducer.min()) - closest_cluster_index_plain = distances_plain.indexOf(min_distance_plain) - - # Create cluster names dictionary - cluster_names = ee.Dictionary( - { - str(i): plain_centroids[str(i)]["cluster_name"] - for i in range(len(plain_centroids)) - } - ) - - # Set cluster index and name - return ( - feature.set("LxP_cluster", closest_cluster_index_plain) - .set( - "clust_name", - cluster_names.get(closest_cluster_index_plain.format()), - ) - .set("barren", plains_barren.multiply(100)) - .set("double_crop", plains_double_crop.multiply(100)) - .set("shrubs_scrubs", plains_shrubs_scrubs.multiply(100)) - .set("sing_crop", plains_single_crop.multiply(100)) - .set("sing_non_kharif_crop", plains_single_non_kharif_crop.multiply(100)) - .set("forest", plains_forest.multiply(100)) - .set("triple_crop", plains_triple_crop.multiply(100)) - ) +import ee +from nrm_app.celery import app +from computing.utils import ( + sync_layer_to_geoserver, + save_layer_info_to_db, + update_layer_sync_status, + create_chunk, + merge_chunks, +) +from utilities.gee_utils import ( + ee_initialize, + check_task_status, + valid_gee_text, + get_gee_asset_path, + is_gee_asset_exists, + export_vector_asset_to_gee, + make_asset_public, + get_gee_dir_path, +) +from .utils import aez_lulcXterrain_cluster_centroids, process_mws, calculate_area +from utilities.constants import AEZ, GEE_HELPER_PATH + + +@app.task(bind=True) +def lulc_on_plain_cluster( + self, state, district, block, start_year, end_year, gee_account_id +): + ee_initialize(gee_account_id) + + asset_description = ( + valid_gee_text(district.lower()) + + "_" + + valid_gee_text(block.lower()) + + "_lulcXplains_clusters_bk02_june" + ) + asset_id = get_gee_asset_path(state, district, block) + asset_description + + if not is_gee_asset_exists(asset_id): + aez_india = ee.FeatureCollection(AEZ) + + landforms = ee.Image( + get_gee_asset_path(state, district, block) + + "terrain_raster_" + + valid_gee_text(district.lower()) + + "_" + + valid_gee_text(block.lower()) + ) # The eleven landforms raster + + mwsheds = ee.FeatureCollection( + get_gee_asset_path(state, district, block) + + "filtered_mws_" + + valid_gee_text(district.lower()) + + "_" + + valid_gee_text(block.lower()) + + "_uid" + ) + + filtered_aez = aez_india.filterBounds(mwsheds.geometry()) + + aez_no = filtered_aez.first().get("ae_regcode").getInfo() + + lulc_imgs = [] + for y in range(start_year, end_year + 1): + lulc_img = ee.Image( + get_gee_asset_path(state, district, block) + + valid_gee_text(district.lower()) + + "_" + + valid_gee_text(block.lower()) + + "_" + + str(y) + + "-07-01_" + + str(y + 1) + + "-06-30_LULCmap_10m" + ) + lulc_imgs.append(lulc_img) + + lulc_img_collection = ee.ImageCollection.fromImages(lulc_imgs) + study_area_lulc = lulc_img_collection.mode().clip(mwsheds) + study_area_landforms = landforms.clip(mwsheds) + + mwsheds_with_clusters = process_mws(mwsheds) + plain_mwsheds = mwsheds_with_clusters.filter( + ee.Filter.neq("terrain_cluster", 2) + ) + plain_centroids = aez_lulcXterrain_cluster_centroids[f"aez{aez_no}"]["plains"] + + chunk_size = 50 + rois, descs = create_chunk(mwsheds, asset_description, chunk_size) + + + tasks = [] + temp_assets = [] + for roi, desc in zip(rois, descs): + chunk_with_clusters = process_mws(roi) + plain_chunk = chunk_with_clusters.filter( + ee.Filter.neq("terrain_cluster", 2) + ) + + + result_chunk = process_feature_collection( + plain_chunk, study_area_landforms, study_area_lulc, plain_centroids + ) + + chunk_asset_id = get_gee_dir_path([state, district, block], GEE_HELPER_PATH) + desc + temp_assets.append(chunk_asset_id) + + + task = export_vector_asset_to_gee( + result_chunk, desc, chunk_asset_id + ) + if task: + tasks.append(task) + + + print("Started all chunk tasks") + task_id_list = check_task_status(tasks) + print("All chunk tasks completed:", task_id_list) + + + # Merge all chunks into one feature collection + print("Starting merge task") + final_task_id = merge_chunks( + mwsheds, + [state, district, block], + asset_description, + chunk_size, + merge_asset_id=asset_id, + ) + if final_task_id: + final_task_status = check_task_status([final_task_id]) + print("Final merge task completed:", final_task_status) + + + # Clean up temporary assets + for chunk_id in temp_assets: + if is_gee_asset_exists(chunk_id): + try: + ee.data.deleteAsset(chunk_id) + print(f"Deleted temp asset {chunk_id}") + except Exception as e: + print(f"Failed to delete {chunk_id}: {e}") + + + layer_at_geoserver = False + if is_gee_asset_exists(asset_id): + layer_id = save_layer_info_to_db( + state, + district, + block, + layer_name=f"{valid_gee_text(district.lower())}_{valid_gee_text(block.lower())}_lulc_plain", + asset_id=asset_id, + dataset_name="Terrain LULC", + misc={ + "start_year": start_year, + "end_year": end_year, + }, + ) + make_asset_public(asset_id) + + fc = ee.FeatureCollection(asset_id).getInfo() + fc = {"features": fc["features"], "type": fc["type"]} + res = sync_layer_to_geoserver( + state, + fc, + valid_gee_text(district.lower()) + + "_" + + valid_gee_text(block.lower()) + + "_lulc_plain", + "terrain_lulc", + ) + print(res) + if res["status_code"] == 201 and layer_id: + update_layer_sync_status(layer_id=layer_id, sync_to_geoserver=True) + print("sync to geoserver flag updated") + layer_at_geoserver = True + return layer_at_geoserver + + +def process_feature_collection(fc, landforms, area_lulc, plain_centroids): + """ + Process an entire FeatureCollection by applying the L2 cluster assignment. + """ + return fc.map(lambda f: assign_l2_cluster(f, landforms, area_lulc, plain_centroids)) + + +def assign_l2_cluster(feature, landforms, area_lulc, plain_centroids): + """ + Assigns L2 clusters to features based on landform and land use characteristics. + """ + study_area = feature.geometry() + lf300x2k = landforms.clip(study_area) + + # Get LULC data + lulc = area_lulc.select("predicted_label") + + # # Convert 10 landforms to 4 general landforms + # slopy = lf300x2k.eq(6) + # plains = lf300x2k.eq(5).Or(lf300x2k.gte(12)) + # steep_slopes = lf300x2k.eq(8) + # ridge = lf300x2k.eq(7).Or(lf300x2k.gte(9).And(lf300x2k.lte(11))) + # valleys = lf300x2k.gte(1).And(lf300x2k.lte(4)) + + # Convert 10 landforms to 4 general landforms + slopy = lf300x2k.eq(6) + plains = lf300x2k.eq(5) + steep_slopes = lf300x2k.eq(8) + # ridge = lf300x2k.gte(9).Or(lf300x2k.eq(7)) + # valleys = lf300x2k.gte(1).And(lf300x2k.lte(4)) + ridge = lf300x2k.eq(3).Or(lf300x2k.eq(7)).Or(lf300x2k.eq(10)).Or(lf300x2k.eq(11)) + valleys = lf300x2k.eq(1).Or(lf300x2k.eq(2)).Or(lf300x2k.eq(4)).Or(lf300x2k.eq(9)) + + # Calculate areas + plain_area = calculate_area(plains, study_area) + valley_area = calculate_area(valleys, study_area) + hill_slopes_area = calculate_area(steep_slopes, study_area) + slopy_area = calculate_area(slopy, study_area) + + plain_plus_slope_area = plain_area.add(slopy_area) + + # Calculate LULC proportions + def calculate_lulc_proportion(lulc_class): + area_image = ( + plains.eq(1) + .And(lulc.eq(lulc_class)) + .multiply(ee.Image.pixelArea()) + .rename("area") + ) + + area = area_image.reduceRegion( + reducer=ee.Reducer.sum(), geometry=study_area, scale=30, maxPixels=1e10 + ) + + return ee.Number(area.get("area")).divide(1e6).divide(plain_plus_slope_area) + + # Calculate all proportions + plains_barren = calculate_lulc_proportion(7) # Barren + plains_double_crop = calculate_lulc_proportion(10) # Double crop + plains_shrubs_scrubs = calculate_lulc_proportion(12) # Shrubs/scrubs + plains_single_crop = calculate_lulc_proportion(8) # Single crop + plains_single_non_kharif_crop = calculate_lulc_proportion(9) # Single non-kharif + plains_forest = calculate_lulc_proportion(6) # Forest + plains_triple_crop = calculate_lulc_proportion(11) # Triple crop + + # Create feature vector + plain_new_feature_vector = ee.List( + [ + plains_barren, + plains_double_crop, + plains_shrubs_scrubs, + plains_single_crop, + plains_single_non_kharif_crop, + plains_forest, + plains_triple_crop, + ] + ) + + # Convert centroids to ee.List format + centroid_vectors = [ + plain_centroids[str(i)]["cluster_vector"] for i in range(len(plain_centroids)) + ] + ee_centroid_vectors = ee.List(centroid_vectors) + + # Calculate distances + def diff_func(value_pair): + return ( + ee.Number(ee.List(value_pair).get(0)) + .subtract(ee.Number(ee.List(value_pair).get(1))) + .pow(2) + ) + + def calculate_distances(centroid): + centroid_list = ee.List(centroid) + paired_values = centroid_list.zip(plain_new_feature_vector) + return paired_values.map(diff_func).reduce(ee.Reducer.sum()) + + distances_plain = ee_centroid_vectors.map(calculate_distances) + + # Find closest cluster + min_distance_plain = distances_plain.reduce(ee.Reducer.min()) + closest_cluster_index_plain = distances_plain.indexOf(min_distance_plain) + + # Create cluster names dictionary + cluster_names = ee.Dictionary( + { + str(i): plain_centroids[str(i)]["cluster_name"] + for i in range(len(plain_centroids)) + } + ) + + # Set cluster index and name + return ( + feature.set("LxP_cluster", closest_cluster_index_plain) + .set( + "clust_name", + cluster_names.get(closest_cluster_index_plain.format()), + ) + .set("barren", plains_barren.multiply(100)) + .set("double_crop", plains_double_crop.multiply(100)) + .set("shrubs_scrubs", plains_shrubs_scrubs.multiply(100)) + .set("sing_crop", plains_single_crop.multiply(100)) + .set("sing_non_kharif_crop", plains_single_non_kharif_crop.multiply(100)) + .set("forest", plains_forest.multiply(100)) + .set("triple_crop", plains_triple_crop.multiply(100)) + ) diff --git a/computing/urls.py b/computing/urls.py index a829c83c..d450e737 100644 --- a/computing/urls.py +++ b/computing/urls.py @@ -238,4 +238,8 @@ api.generate_canal_vector, name="generate-canal-vector", ), + path("refresh_cache/", api.refresh_layer_cache, name="refresh_cache"), + path( + "refresh_cache//", api.refresh_layer_cache, name="refresh_cache" + ), ] diff --git a/computing/utils.py b/computing/utils.py index b14d063c..da6b632b 100644 --- a/computing/utils.py +++ b/computing/utils.py @@ -1,1032 +1,1121 @@ -import copy -import json -import logging -import os -import shutil -import zipfile -from datetime import datetime, timedelta - -import ee -import fiona -import geopandas as gpd -import requests -from django.conf import settings -from shapely.geometry import shape -from shapely.validation import explain_validity - -from computing.models import Dataset, Layer -from geoadmin.models import ( - DistrictSOI, - State_Disritct_Block_Properties, - StateSOI, - TehsilSOI, -) -from projects.models import Project -from utilities.constants import ( - ADMIN_BOUNDARY_OUTPUT_DIR, - GEE_ASSET_PATH, - GEE_HELPER_PATH, - GEE_PATHS, - SHAPEFILE_DIR, -) -from utilities.gee_utils import ( - check_task_status, - ee_initialize, - get_gee_asset_path, - get_gee_dir_path, - get_geojson_from_gcs, - is_asset_public, - is_gee_asset_exists, - sync_vector_to_gcs, - valid_gee_text, -) -from utilities.geoserver_utils import Geoserver - -logger = logging.getLogger(__name__) - - -def generate_shape_files(path): - gdf = gpd.read_file(path + ".json") - if os.path.exists(path): - # Only replace the target shapefile directory. Removing the parent - # state/workspace directory here corrupts sibling outputs on reruns. - shutil.rmtree(path) - - os.makedirs(os.path.dirname(path), exist_ok=True) - gdf.to_file( - path, - driver="ESRI Shapefile", - ) - return path - - -def convert_to_zip(dir_name, file_type): - if file_type == "gpkg": - with zipfile.ZipFile(dir_name + ".zip", "w", zipfile.ZIP_DEFLATED) as zipf: - zipf.write(dir_name + ".gpkg", arcname=os.path.basename(dir_name + ".gpkg")) - return dir_name + ".zip" - else: - return shutil.make_archive(dir_name, "zip", dir_name + "/") - - -def push_shape_to_geoserver( - path, store_name=None, workspace=None, layer_name=None, file_type="shp" -): - geo = Geoserver() - - print(f"layer_name: {layer_name}") - if layer_name: - try: - print(f"Attempting to delete store: {layer_name}") - geo.delete_vector_store(workspace=workspace, store=layer_name) - print(f"Successfully deleted store: {layer_name}") - except Exception as e: - print(f"Store does not exist or error deleting: {str(e)}") - - zip_path = convert_to_zip(path, file_type) - print(f"Zip path: {zip_path}") - print(f"Store name: {store_name}") - print(f"Workspace: {workspace}") - - response = geo.create_shp_datastore( - path=zip_path, - store_name=store_name, - workspace=workspace, - file_extension=file_type, - ) - print(f"Response: {response}") - return response - - -def kml_to_geojson(state_name, district_name, block_name, kml_path): - fiona.drvsupport.supported_drivers["kml"] = ( - "rw" # enable KML support which is disabled by default - ) - fiona.drvsupport.supported_drivers["KML"] = ( - "rw" # enable KML support which is disabled by default - ) - gdf = gpd.read_file(kml_path) - geometry_types = gdf.geometry.geometry.type.unique() - state_dir = os.path.join(ADMIN_BOUNDARY_OUTPUT_DIR, state_name) - - for gtype in geometry_types: - df = gdf.loc[gdf.geometry.geometry.type == gtype] - path = os.path.join(state_dir, f"{district_name}_{block_name}_{gtype}") - df.to_file(path + ".json", driver="GeoJSON") - generate_shape_files(path) - push_shape_to_geoserver(path, workspace="test_workspace") - - -def convert_kml_to_shapefile(kml_path, output_dir, shapefile_name): - if not os.path.exists(output_dir + "/" + shapefile_name): - os.makedirs(output_dir + "/" + shapefile_name) - - shapefile_path = os.path.join( - output_dir + "/" + shapefile_name, shapefile_name + ".shp" - ) - print("path path", shapefile_path) - cmd = f"ogr2ogr -f 'ESRI Shapefile' {shapefile_path} {kml_path}" # output.shp input.kml - os.system(command=cmd) - - return output_dir + "/" + shapefile_name - - -def kml_to_shp(state_name, district_name, block_name, kml_path): - shapefile_name = f"{district_name}_{block_name}" - shapefile_layer_path = convert_kml_to_shapefile( - kml_path, SHAPEFILE_DIR, shapefile_name - ) - - push_shape_to_geoserver(shapefile_layer_path, workspace="customkml") - - # os.remove(kml_path) - # shutil.rmtree(shapefile_layer_path) - os.remove(shapefile_layer_path + ".zip") - - -def sync_layer_to_geoserver(state_name, fc, layer_name, workspace): - state_dir = os.path.join("data/fc_to_shape", state_name) - if not os.path.exists(state_dir): - os.mkdir(state_dir) - path = os.path.join(state_dir, f"{layer_name}") - # Write the feature collection into json file - with open(path + ".json", "w") as f: - try: - f.write(f"{json.dumps(fc)}") - except Exception as e: - print(e) - - path = generate_shape_files(path) - return push_shape_to_geoserver(path, workspace=workspace, layer_name=layer_name) - - -def sync_fc_to_geoserver(fc, shp_folder, layer_name, workspace, style_name=None): - try: - geojson_fc = fc.getInfo() - except Exception as e: - print("Exception in getInfo()", e) - task_id = sync_vector_to_gcs(fc, layer_name, "GeoJSON") - check_task_status([task_id]) - - geojson_fc = get_geojson_from_gcs(layer_name) - geo = Geoserver() - if len(geojson_fc["features"]) > 0: - state_dir = os.path.join("data/fc_to_shape", shp_folder) - if not os.path.exists(state_dir): - os.mkdir(state_dir) - path = os.path.join(state_dir, f"{layer_name}") - - # Convert to GeoDataFrame - gdf = gpd.GeoDataFrame.from_features(geojson_fc["features"]) - - # Set CRS (Earth Engine uses EPSG:4326 by default) - gdf.crs = "EPSG:4326" - - gdf = fix_invalid_geometry_in_gdf(gdf) - - # Save as GeoPackage - gdf.to_file(path + ".gpkg", driver="GPKG") - res = push_shape_to_geoserver(path, workspace=workspace, file_type="gpkg") - if style_name: - style_res = geo.publish_style( - layer_name=layer_name, style_name=style_name, workspace=workspace - ) - print("Style response:", style_res) - return res - else: - return "No features in FeatureCollection" - - -def sync_project_fc_to_geoserver(fc, project_name, layer_name, workspace): - print("inside") - print(layer_name) - try: - geojson_fc = fc.getInfo() - except Exception as e: - print("Exception in getInfo()", e) - task_id = sync_vector_to_gcs(fc, layer_name, "GeoJSON") - check_task_status([task_id]) - - geojson_fc = get_geojson_from_gcs(layer_name) - print(len(geojson_fc["features"])) - if len(geojson_fc["features"]) > 0: - state_dir = os.path.join("data/fc_to_shape", project_name) - if not os.path.exists(state_dir): - os.mkdir(state_dir) - path = os.path.join(state_dir, f"{layer_name}") - - # Convert to GeoDataFrame - gdf = gpd.GeoDataFrame.from_features(geojson_fc["features"]) - - # Set CRS (Earth Engine uses EPSG:4326 by default) - gdf.crs = "EPSG:4326" - - gdf = fix_invalid_geometry_in_gdf(gdf) - - # Save as GeoPackage - gdf.to_file(path + ".gpkg", driver="GPKG") - print("pushed to geoserver") - return push_shape_to_geoserver( - path, workspace=workspace, layer_name=layer_name, file_type="gpkg" - ) - else: - print("no features found") - return - - -def to_camelcase(text): - words = text.split() - camelcase = words[0].lower() - for word in words[1:]: - camelcase += word.capitalize() - return camelcase - - -def create_chunk(aoi, description, chunk_size): - size = aoi.size().getInfo() - parts = size // chunk_size - # task_ids = [] - rois = [] - descs = [] - for part in range(parts + 1): - start = part * chunk_size - end = start + chunk_size - block_name_for_parts = description + "_" + str(start) + "-" + str(end) - roi = ee.FeatureCollection(aoi.toList(aoi.size()).slice(start, end)) - if roi.size().getInfo() > 0: - descs.append(block_name_for_parts) - rois.append(roi) - - return rois, descs - - -def merge_chunks( - aoi, - folder_list, - description, - chunk_size, - chunk_asset_path=GEE_HELPER_PATH, - merge_asset_path=GEE_ASSET_PATH, - merge_asset_id=None, -): - print("Merge Chunk task initiated") - ee_initialize() - size = aoi.size().getInfo() - parts = size // chunk_size - assets = [] - for part in range(parts + 1): - start = part * chunk_size - end = start + chunk_size - block_name_for_parts = description + "_" + str(start) + "-" + str(end) - src_asset_id = ( - get_gee_dir_path(folder_list, chunk_asset_path) + block_name_for_parts - ) - if is_gee_asset_exists(src_asset_id): - assets.append(ee.FeatureCollection(src_asset_id)) - - asset = ee.FeatureCollection(assets).flatten() - - asset_id = merge_asset_id or ( - get_gee_dir_path(folder_list, merge_asset_path) + description - ) - try: - # Export an ee.FeatureCollection as an Earth Engine asset. - task = ee.batch.Export.table.toAsset( - **{ - "collection": asset, - "description": description, - "assetId": asset_id, - } - ) - - task.start() - print("Successfully started the merge chunk", task.status()) - return task.status()["id"] - except Exception as e: - print(f"Error occurred in running merge task: {e}") - return None - - -def fix_invalid_geometry_in_gdf(gdf): - invalid = gdf[~gdf.is_valid] - if not invalid.empty: - print("Invalid geometries found:") - for idx, geom in invalid.geometry.items(): - print(f"Index {idx}: {explain_validity(geom)}") - gdf.loc[idx, "geometry"] = gdf.loc[idx, "geometry"].buffer(0) - - return gdf - - -def get_season_key(date): - """Return season key like 'rabi_2017-2018' based on Indian cropping seasons.""" - month = date.month - year = date.year - next_year = year + 1 - - if month in [1, 2]: - return f"rabi_{year - 1}-{year}" # Jan–Feb → Rabi of previous year - elif month in [11, 12]: - return f"rabi_{year}-{next_year}" # Nov–Dec → Rabi starting this year - elif month in [3, 4, 5, 6]: - return f"zaid_{year}-{next_year}" - elif month in [7, 8, 9, 10]: - return f"kharif_{year}-{next_year}" - else: - return None - - -def get_agri_year_key(season_key): - """Convert a season key to agricultural year key (e.g., rabi_2017-2018 → 2017-2018).""" - season, years = season_key.split("_") - start_year, end_year = map(int, years.split("-")) - - if season in ["kharif", "rabi"]: - return f"{start_year}-{end_year}" - elif season == "zaid": - return f"{start_year - 1}-{start_year}" # Zaid 2018-2019 → Agri year 2017-2018 - else: - return None - - -def calculate_precipitation_season( - geojson_filepath, draught_asset_id, start_year=2017, end_year=2024 -): - - # Load the GeoJSON file - with open(geojson_filepath, "r") as f: - feature_collection = json.load(f) - - features_ee = [] - - for feature in feature_collection["features"]: - original_props = feature["properties"] - new_props = {} - - # Copy UID - if "uid" in original_props: - new_props["uid"] = original_props["uid"] - - agri_year_totals = {} - - # Parse precipitation date keys - for key, val in original_props.items(): - try: - date = datetime.strptime(key, "%Y-%m-%d") - season_key = get_season_key(date) - if not season_key: - continue - - agri_key = get_agri_year_key(season_key) - if not agri_key: - continue - - agri_start = int(agri_key.split("-")[0]) - if not (start_year <= agri_start <= end_year): - continue - - season = season_key.split("_")[0] # kharif, rabi etc - full_key = f"{season}_{agri_key}" - - agri_year_totals[full_key] = agri_year_totals.get(full_key, 0) + float( - val - ) - - except Exception: - continue - - # Add all seasonal totals to new_props - for agri_key, total in agri_year_totals.items(): - new_props[f"precipitation_{agri_key}"] = total - - # Create EE Feature - geom_ee = ee.Geometry(feature["geometry"]) - feature_ee = ee.Feature(geom_ee, new_props) - features_ee.append(feature_ee) - - # Left side FC - mws_fc = ee.FeatureCollection(features_ee) - - return mws_fc - - -def generate_geojson_with_ci_and_ndvi(zoi_asset, ci_asset, ndvi_asset, proj_id): - # Load project - proj_obj = Project.objects.get(pk=proj_id) - - # Build CI and NDVI asset paths - asset_path_ci = ( - get_gee_dir_path( - [proj_obj.name], asset_path=GEE_PATHS["WATER_REJ"]["GEE_ASSET_PATH"] - ) - + ci_asset - ) - - asset_path_ndvi = ( - get_gee_dir_path( - [proj_obj.name], asset_path=GEE_PATHS["WATER_REJ"]["GEE_ASSET_PATH"] - ) - + ndvi_asset - ) - - # Load FeatureCollections - zoi = ee.FeatureCollection(zoi_asset) - ci = ee.FeatureCollection(asset_path_ci) - ndvi = ee.FeatureCollection(asset_path_ndvi) - - # ------------------------- - # STEP 1: Join ZOI with Cropping Intensity - # ------------------------- - join = ee.Join.inner() - filter = ee.Filter.intersects(leftField=".geo", rightField=".geo") - zoi_ci_joined = join.apply(zoi, ci, filter) - - def merge_zoi_ci(pair): - zoi_feat = ee.Feature(pair.get("primary")) - ci_feat = ee.Feature(pair.get("secondary")) - merged_props = zoi_feat.toDictionary().combine(ci_feat.toDictionary(), True) - return ee.Feature(zoi_feat.geometry(), merged_props) - - zoi_with_ci = ee.FeatureCollection(zoi_ci_joined.map(merge_zoi_ci)) - - # ------------------------- - # STEP 2: Join ZOI+CI with NDVI - # ------------------------- - zoi_ndvi_joined = join.apply(zoi_with_ci, ndvi, filter) - - def merge_zoi_ci_ndvi(pair): - ci_feat = ee.Feature(pair.get("primary")) - ndvi_feat = ee.Feature(pair.get("secondary")) - merged_props = ci_feat.toDictionary().combine(ndvi_feat.toDictionary(), True) - return ee.Feature(ci_feat.geometry(), merged_props) - - final_merged = ee.FeatureCollection(zoi_ndvi_joined.map(merge_zoi_ci_ndvi)) - - # ------------------------- - # STEP 3: Export or Push to GeoServer - # ------------------------- - layer_name = f"WaterRejapp_zoi_{proj_obj.name}_{proj_obj.id}" - sync_project_fc_to_geoserver(final_merged, proj_obj.name, layer_name, "waterrej") - - -def get_directory_size(path): - total_size = 0 - for dirpath, dirnames, filenames in os.walk(path): - for filename in filenames: - file_path = os.path.join(dirpath, filename) - if os.path.isfile(file_path): - total_size += os.path.getsize(file_path) - return total_size - - -def generate_geojson_with_ci_ndvi_ndmi( - zoi_asset, ci_asset, ndvi_asset, ndmi_asset, proj_id -): - - # Load project - proj_obj = Project.objects.get(pk=proj_id) - - zoi = ee.FeatureCollection(zoi_asset) - print("Number of features zoi:", zoi.size().getInfo()) - - ci = ee.FeatureCollection(ci_asset) - print("Number of features zoi:", ci.size().getInfo()) - ndvi = ee.FeatureCollection(ndmi_asset) - print("Number of features zoi:", ndvi.size().getInfo()) - ndmi = ee.FeatureCollection(ndmi_asset) - print("Number of features zoi:", ndmi.size().getInfo()) - - # ------------------------- - # STEP 1: Join ZOI with CI - # ------------------------- - join = ee.Join.inner() - filter = ee.Filter.intersects(leftField=".geo", rightField=".geo") - zoi_ci_joined = join.apply(zoi, ci, filter) - - def merge_zoi_ci(pair): - zoi_feat = ee.Feature(pair.get("primary")) - ci_feat = ee.Feature(pair.get("secondary")) - merged_props = zoi_feat.toDictionary().combine(ci_feat.toDictionary(), True) - return ee.Feature(zoi_feat.geometry(), merged_props) # ✅ keep ZOI geom - - zoi_with_ci = ee.FeatureCollection(zoi_ci_joined.map(merge_zoi_ci)) - - # ------------------------- - # STEP 2: Join with NDVI - # ------------------------- - zoi_ndvi_joined = join.apply(zoi_with_ci, ndvi, filter) - - def merge_zoi_ci_ndvi(pair): - prev_feat = ee.Feature(pair.get("primary")) - ndvi_feat = ee.Feature(pair.get("secondary")) - merged_props = prev_feat.toDictionary().combine(ndvi_feat.toDictionary(), True) - return ee.Feature(prev_feat.geometry(), merged_props) # ✅ still ZOI geom - - zoi_ci_ndvi = ee.FeatureCollection(zoi_ndvi_joined.map(merge_zoi_ci_ndvi)) - - # ------------------------- - # STEP 3: Join with NDMI - # ------------------------- - zoi_ndmi_joined = join.apply(zoi_ci_ndvi, ndmi, filter) - - def merge_zoi_ci_ndvi_ndmi(pair): - prev_feat = ee.Feature(pair.get("primary")) - ndmi_feat = ee.Feature(pair.get("secondary")) - merged_props = prev_feat.toDictionary().combine(ndmi_feat.toDictionary(), True) - return ee.Feature(prev_feat.geometry(), merged_props) # ✅ keep ZOI geom - - final_merged = ee.FeatureCollection(zoi_ndmi_joined.map(merge_zoi_ci_ndvi_ndmi)) - - # ------------------------- - # STEP 4: Export or Push to GeoServer - # ------------------------- - layer_name = f"WaterRejapp_zoi_{proj_obj.name}_{proj_obj.id}" - print(layer_name) - sync_project_fc_to_geoserver(final_merged, proj_obj.name, layer_name, "waterrej") - - -def generate_geojson_with_ci_ndvi(zoi_asset, ci_asset, ndvi_asset, proj_id): - # Load project - proj_obj = Project.objects.get(pk=proj_id) - - # Initialize Earth Engine - ee_initialize(4) - - # Load FeatureCollections - zoi = ee.FeatureCollection(zoi_asset) - ci = ee.FeatureCollection(ci_asset) - ndvi = ee.FeatureCollection(ndvi_asset) - - print("ZOI:", zoi.size().getInfo()) - print("CI:", ci.size().getInfo()) - print("NDVI:", ndvi.size().getInfo()) - - # Common join logic on UID - join = ee.Join.inner() - uid_filter = ee.Filter.equals(leftField="UID", rightField="UID") - - # --- Join ZOI + CI --- - zoi_ci_joined = join.apply(zoi, ci, uid_filter) - - def merge_zoi_ci(pair): - zoi_feat = ee.Feature(pair.get("primary")) - ci_feat = ee.Feature(pair.get("secondary")) - merged_props = zoi_feat.toDictionary().combine(ci_feat.toDictionary(), True) - # Keep ZOI geometry only - return ee.Feature(zoi_feat.geometry(), merged_props) - - zoi_with_ci = ee.FeatureCollection(zoi_ci_joined.map(merge_zoi_ci)) - - # --- Join with NDVI --- - zoi_ndvi_joined = join.apply(zoi_with_ci, ndvi, uid_filter) - - def merge_with_ndvi(pair): - base_feat = ee.Feature(pair.get("primary")) - ndvi_feat = ee.Feature(pair.get("secondary")) - merged_props = base_feat.toDictionary().combine(ndvi_feat.toDictionary(), True) - # Always retain ZOI geometry - return ee.Feature(base_feat.geometry(), merged_props) - - merged_final = ee.FeatureCollection(zoi_ndvi_joined.map(merge_with_ndvi)) - - # --- Ensure ZOI geometry retained in all features --- - merged_final = merged_final.map( - lambda f: ee.Feature( - f.setGeometry( - ee.Feature( - zoi.filter(ee.Filter.eq("UID", f.get("UID"))).first() - ).geometry() - ) - ) - ) - - layer_name = f"WaterRejapp_zoi_{proj_obj.name}_{proj_obj.id}" - print(layer_name) - - sync_project_fc_to_geoserver(merged_final, proj_obj.name, layer_name, "waterrej") - - -def save_layer_info_to_db( - state, - district, - block, - layer_name, - asset_id, - dataset_name, - sync_to_geoserver=False, - layer_version="1.0", - algorithm=None, - algorithm_version="1.0", - misc=None, - is_override=False, -): - print("inside the save_layer_info_to_db function") - - dataset = Dataset.objects.get(name=dataset_name) - - try: - state_obj = StateSOI.objects.get(state_name__iexact=state) - district_obj = DistrictSOI.objects.get( - district_name__iexact=district, state=state_obj - ) - block_obj = TehsilSOI.objects.get( - tehsil_name__iexact=block, district=district_obj - ) - except Exception as e: - print("Error fetching in state district block:", e) - return - - is_public = is_asset_public(asset_id) - - # Check if there’s an existing layer - existing_layer = ( - Layer.objects.filter( - dataset=dataset, - layer_name=layer_name, - state=state_obj, - district=district_obj, - block=block_obj, - ) - .order_by("-layer_version") - .first() - ) - - if existing_layer: - if existing_layer.algorithm_version != algorithm_version: - # Algorithm version changed --> create new record with incremented layer_version - new_layer_version = str(float(existing_layer.layer_version) + 1) - print( - f"Algorithm version changed. Creating new layer version: {new_layer_version}" - ) - layer_obj = Layer.objects.create( - dataset=dataset, - layer_name=layer_name, - state=state_obj, - district=district_obj, - block=block_obj, - layer_version=new_layer_version, - algorithm=algorithm, - algorithm_version=algorithm_version, - is_sync_to_geoserver=sync_to_geoserver, - is_public_gee_asset=is_public, - is_override=is_override, - misc=misc, - gee_asset_path=asset_id, - ) - else: - # Algorithm version is same --> update existing layer - print("Algorithm version same. Updating existing layer.") - for field, value in { - "algorithm": algorithm, - "algorithm_version": algorithm_version, - "is_sync_to_geoserver": sync_to_geoserver, - "is_public_gee_asset": is_public, - "is_override": is_override, - "misc": misc, - "gee_asset_path": asset_id, - }.items(): - setattr(existing_layer, field, value) - existing_layer.save() - layer_obj = existing_layer - else: - # No existing record --> create a new one - print("No existing layer found. Creating new one.") - layer_obj = Layer.objects.create( - dataset=dataset, - layer_name=layer_name, - state=state_obj, - district=district_obj, - block=block_obj, - layer_version=layer_version, - algorithm=algorithm, - algorithm_version=algorithm_version, - is_sync_to_geoserver=sync_to_geoserver, - is_public_gee_asset=is_public, - is_override=is_override, - misc=misc, - gee_asset_path=asset_id, - ) - - print(f"Saved layer info (id={layer_obj.id}, version={layer_obj.layer_version})") - return layer_obj.id - - -def get_existing_end_year(dataset_name, layer_name): - """fetch objects from db on the basis of dataset name and layer_name""" - dataset = Dataset.objects.get(name=dataset_name) - layer_obj = Layer.objects.get(dataset=dataset, layer_name=layer_name) - existing_end_date = layer_obj.misc["end_year"] - print("existing_end_date", existing_end_date) - return existing_end_date - - -def get_layer_object(state, district, block, layer_name, dataset_name): - state_obj = StateSOI.objects.get(state_name__iexact=state) - district_obj = DistrictSOI.objects.get( - district_name__iexact=district, state=state_obj - ) - block_obj = TehsilSOI.objects.get(tehsil_name__iexact=block, district=district_obj) - layer_obj = ( - Layer.objects.filter( - state=state_obj, - district=district_obj, - block=block_obj, - layer_name=layer_name, - dataset__name=dataset_name, - ) - .order_by("-layer_version") - .first() - ) - return layer_obj - - -def update_dashboard_geojson( - state=None, - district=None, - block=None, - layer_name=None, - workspace_name=None, - proj_id=None, -): - if state and block and block: - print(f"🔄 Updating GeoJSON for {state}, {district}, {block}") - - # Get related objects - state_obj = StateSOI.objects.get(state_name=state) - district_obj = DistrictSOI.objects.get(district_name=district) - tehsil_obj = TehsilSOI.objects.get(tehsil_name=block) # fixed typo - - # Get or create main record - obj, created = State_Disritct_Block_Properties.objects.get_or_create( - state=state_obj, district=district_obj, tehsil=tehsil_obj - ) - else: - obj = Project.objects.get(pk=proj_id) - - # Map suffix to json_key - suffix_to_key = { - "wb": "wb_geojson", - "zoi": "zoi_geojson", - "mws": "mws_geojson", - } - - # Detect which key this layer corresponds to - json_key = None - for suffix, key in suffix_to_key.items(): - if layer_name == f"{state}_{district}_{block}_{suffix}": - json_key = key - break - - if not json_key: - print(f"⚠️ Layer name {layer_name} did not match any known type.") - return - - # Construct GeoServer URL - waterrej_url = ( - f"https://geoserver.core-stack.org:8443/geoserver/waterrej/ows?" - f"service=WFS&version=1.0.0&request=GetFeature&typeName={workspace_name}:{layer_name}" - f"&outputFormat=application%2Fjson" - ) - - # Load existing dashboard_geojson or create new - if proj_id: - misc = obj.dashboard_geojson or {} - else: - misc = obj.geojson_path or {} - - # Ensure waterrej section exists - if "waterrej" not in misc: - misc["waterrej"] = {} - - # Update or add this specific json_key - misc["waterrej"][json_key] = waterrej_url - - # Save the updated JSON field - obj.dashboard_geojson = misc - obj.save() - - print(f"✅ Added/Updated {json_key} for {state}, {district}, {block}") - - -def clean_geometry(geom): - """ - Clean geometry: - - Dissolve multipolygon → single polygon - - Remove holes automatically - - Fix invalid topology - - Buffer tiny polygons - """ - - # 1. Dissolve multi-polygons and remove holes - geom = geom.dissolve(maxError=1) - - # 2. Fix invalid rings by simplifying slightly (NEVER buffer(0)) - geom = geom.simplify(1) - - # 3. Buffer polygons smaller than 1 pixel (< 900 m²) - area = geom.area() - geom = ee.Algorithms.If( - area.lt(900), - geom.buffer(15), - geom, # ensure raster pixel center is captured - ) - - return ee.Geometry(geom) - - -def safe_reduce_max(image, geom, scale=30): - geom = clean_geometry(geom) - - val = ( - image.unmask(0) - .reduceRegion( - reducer=ee.Reducer.max(), - geometry=geom, - scale=scale, - maxPixels=1e13, - tileScale=4, - bestEffort=True, - ) - .get("b1") - ) - - return ee.Number(ee.Algorithms.If(val, val, 0)) - - -# ------------------------------------------------------ -# SAFE REDUCE MAX FUNCTION -# ------------------------------------------------------ -def safe_reduce_max(image, geom, scale=30): - geom = clean_geometry(geom) - - result = ( - image.unmask(0) - .reduceRegion( - reducer=ee.Reducer.max(), - geometry=geom, - scale=scale, - maxPixels=1e13, - tileScale=4, - bestEffort=True, - ) - .get("b1") - ) - - # Convert null → 0 - return ee.Number(ee.Algorithms.If(result, result, 0)) - - -# ------------------------------------------------------ -# MAIN FUNCTION TO PROCESS SWB LAYER -# ------------------------------------------------------ -def generate_swb_layer_with_max_so_catchment( - roi=None, - app_type="MWS", - asset_suffix=None, - asset_folder=None, - gee_account_id=None, -): - ee_initialize(gee_account_id) - - # Build asset paths - base_path = get_gee_dir_path( - asset_folder, asset_path=GEE_PATHS[app_type]["GEE_ASSET_PATH"] - ) - - so_asset = f"{base_path}stream_order_{asset_suffix}_raster" - ca_asset = f"{base_path}catchment_area_{asset_suffix}_raster" - - # Load rasters - stream_order_band = ee.Image(so_asset).select("b1") - catchment_band = ee.Image(ca_asset).select("b1") - - # Processing per waterbody - def compute_for_feature(feature): - geom = feature.geometry() - - max_so = safe_reduce_max(stream_order_band, geom, scale=30) - max_ca = safe_reduce_max(catchment_band, geom, scale=30) - - return feature.set( - { - "max_stream_order": max_so, - "max_catchment_area": max_ca, - } - ) - - # Map over the feature collection - return roi.map(compute_for_feature) - - -def _get_prod_backend_url(): - return getattr(settings, "PROD_BACKEND_URL", "").rstrip("/") - - -def _get_prod_api_key(): - return getattr(settings, "PROD_BACKEND_API_KEY", "") - - -def _sync_layer_to_prod_db(payload: dict): - prod_url = _get_prod_backend_url() - if not prod_url: - return None - - endpoint = prod_url + "/api/v1/sync_layer_remote/" - try: - response = requests.post( - endpoint, - json=payload, - headers={"X-Api-Key": _get_prod_api_key()}, - timeout=30, - ) - if response.status_code not in (200, 201): - logger.warning( - "Prod DB sync returned %s for layer %s: %s", - response.status_code, - payload.get("layer_name"), - response.text, - ) - return None - layer_id = response.json().get("layer_id") - logger.info( - "Layer %s synced to prod DB (id=%s).", payload.get("layer_name"), layer_id - ) - return layer_id - except requests.RequestException as e: - logger.error( - "Failed to sync layer %s to prod DB: %s", payload.get("layer_name"), e - ) - return None - - -def _update_layer_sync_remote( - layer_id, sync_to_geoserver=None, is_stac_specs_generated=None -): - prod_url = _get_prod_backend_url() - if not prod_url or layer_id is None: - return - - endpoint = prod_url + "/api/v1/update_layer_sync_remote/" - payload = { - "layer_id": layer_id, - "sync_to_geoserver": sync_to_geoserver, - "is_stac_specs_generated": is_stac_specs_generated, - } - try: - response = requests.post( - endpoint, - json=payload, - headers={"X-Api-Key": _get_prod_api_key()}, - timeout=30, - ) - if response.status_code not in (200, 201): - logger.warning( - "Prod layer sync status update returned %s for layer %s: %s", - response.status_code, - layer_id, - response.text, - ) - else: - logger.info("Layer sync status updated on prod DB for id=%s.", layer_id) - except requests.RequestException as e: - logger.error( - "Failed to update layer sync status on prod DB for id=%s: %s", layer_id, e - ) - - -def update_layer_sync_status( - layer_id, sync_to_geoserver=None, is_stac_specs_generated=None -): - if _get_prod_backend_url(): - _update_layer_sync_remote( - layer_id, - sync_to_geoserver=sync_to_geoserver, - is_stac_specs_generated=is_stac_specs_generated, - ) - return layer_id - - try: - layer_obj = Layer.objects.filter(id=layer_id).first() - if layer_obj is None: - return None - - update_fields = [] - if sync_to_geoserver is not None: - layer_obj.is_sync_to_geoserver = sync_to_geoserver - update_fields.append("is_sync_to_geoserver") - if is_stac_specs_generated is not None: - layer_obj.is_stac_specs_generated = is_stac_specs_generated - update_fields.append("is_stac_specs_generated") - - # `save(update_fields=...)` fires the post_save signal so the STAC - # auto-trigger handler in `computing.signals` can pick up the flip. - if update_fields: - layer_obj.save(update_fields=update_fields) - print( - f"Updated {update_fields} for layer ID: {layer_id} " - f"(sync={sync_to_geoserver}, stac={is_stac_specs_generated})" - ) - return layer_id - - except Exception as e: - print(f"Error updating layer sync status: {e}") +import copy +import json +import logging +import os +import shutil +import zipfile +from datetime import datetime, timedelta + +import ee +import fiona +import geopandas as gpd +import requests +from django.conf import settings +from shapely.geometry import shape +from shapely.validation import explain_validity + +from computing.models import Dataset, Layer +from geoadmin.models import ( + DistrictSOI, + State_Disritct_Block_Properties, + StateSOI, + TehsilSOI, +) +from projects.models import Project +from utilities.constants import ( + ADMIN_BOUNDARY_OUTPUT_DIR, + GEE_ASSET_PATH, + GEE_HELPER_PATH, + GEE_PATHS, + SHAPEFILE_DIR, +) +from utilities.gee_utils import ( + check_task_status, + ee_initialize, + get_gee_asset_path, + get_gee_dir_path, + get_geojson_from_gcs, + is_asset_public, + is_gee_asset_exists, + sync_vector_to_gcs, + valid_gee_text, +) +from utilities.geoserver_utils import Geoserver +from django.core.mail import EmailMessage, get_connection +import time + +logger = logging.getLogger(__name__) + + +def generate_shape_files(path): + gdf = gpd.read_file(path + ".json") + if os.path.exists(path): + # Only replace the target shapefile directory. Removing the parent + # state/workspace directory here corrupts sibling outputs on reruns. + shutil.rmtree(path) + + os.makedirs(os.path.dirname(path), exist_ok=True) + gdf.to_file( + path, + driver="ESRI Shapefile", + ) + return path + + +def convert_to_zip(dir_name, file_type): + if file_type == "gpkg": + with zipfile.ZipFile(dir_name + ".zip", "w", zipfile.ZIP_DEFLATED) as zipf: + zipf.write(dir_name + ".gpkg", arcname=os.path.basename(dir_name + ".gpkg")) + return dir_name + ".zip" + else: + return shutil.make_archive(dir_name, "zip", dir_name + "/") + + +def push_shape_to_geoserver( + path, store_name=None, workspace=None, layer_name=None, file_type="shp" +): + geo = Geoserver() + + print(f"layer_name: {layer_name}") + if layer_name: + try: + print(f"Attempting to delete store: {layer_name}") + geo.delete_vector_store(workspace=workspace, store=layer_name) + print(f"Successfully deleted store: {layer_name}") + except Exception as e: + print(f"Store does not exist or error deleting: {str(e)}") + + zip_path = convert_to_zip(path, file_type) + print(f"Zip path: {zip_path}") + print(f"Store name: {store_name}") + print(f"Workspace: {workspace}") + + response = geo.create_shp_datastore( + path=zip_path, + store_name=store_name, + workspace=workspace, + file_extension=file_type, + ) + print(f"Response: {response}") + return response + + +def kml_to_geojson(state_name, district_name, block_name, kml_path): + fiona.drvsupport.supported_drivers["kml"] = ( + "rw" # enable KML support which is disabled by default + ) + fiona.drvsupport.supported_drivers["KML"] = ( + "rw" # enable KML support which is disabled by default + ) + gdf = gpd.read_file(kml_path) + geometry_types = gdf.geometry.geometry.type.unique() + state_dir = os.path.join(ADMIN_BOUNDARY_OUTPUT_DIR, state_name) + + for gtype in geometry_types: + df = gdf.loc[gdf.geometry.geometry.type == gtype] + path = os.path.join(state_dir, f"{district_name}_{block_name}_{gtype}") + df.to_file(path + ".json", driver="GeoJSON") + generate_shape_files(path) + push_shape_to_geoserver(path, workspace="test_workspace") + + +def convert_kml_to_shapefile(kml_path, output_dir, shapefile_name): + if not os.path.exists(output_dir + "/" + shapefile_name): + os.makedirs(output_dir + "/" + shapefile_name) + + shapefile_path = os.path.join( + output_dir + "/" + shapefile_name, shapefile_name + ".shp" + ) + print("path path", shapefile_path) + cmd = f"ogr2ogr -f 'ESRI Shapefile' {shapefile_path} {kml_path}" # output.shp input.kml + os.system(command=cmd) + + return output_dir + "/" + shapefile_name + + +def kml_to_shp(state_name, district_name, block_name, kml_path): + shapefile_name = f"{district_name}_{block_name}" + shapefile_layer_path = convert_kml_to_shapefile( + kml_path, SHAPEFILE_DIR, shapefile_name + ) + + push_shape_to_geoserver(shapefile_layer_path, workspace="customkml") + + # os.remove(kml_path) + # shutil.rmtree(shapefile_layer_path) + os.remove(shapefile_layer_path + ".zip") + + +def sync_layer_to_geoserver(state_name, fc, layer_name, workspace): + state_dir = os.path.join("data/fc_to_shape", state_name) + if not os.path.exists(state_dir): + os.mkdir(state_dir) + path = os.path.join(state_dir, f"{layer_name}") + # Write the feature collection into json file + with open(path + ".json", "w") as f: + try: + f.write(f"{json.dumps(fc)}") + except Exception as e: + print(e) + + path = generate_shape_files(path) + return push_shape_to_geoserver(path, workspace=workspace, layer_name=layer_name) + + +def sync_fc_to_geoserver(fc, shp_folder, layer_name, workspace, style_name=None): + try: + geojson_fc = fc.getInfo() + except Exception as e: + print("Exception in getInfo()", e) + task_id = sync_vector_to_gcs(fc, layer_name, "GeoJSON") + check_task_status([task_id]) + + geojson_fc = get_geojson_from_gcs(layer_name) + geo = Geoserver() + if len(geojson_fc["features"]) > 0: + state_dir = os.path.join("data/fc_to_shape", shp_folder) + if not os.path.exists(state_dir): + os.mkdir(state_dir) + path = os.path.join(state_dir, f"{layer_name}") + + # Convert to GeoDataFrame + gdf = gpd.GeoDataFrame.from_features(geojson_fc["features"]) + + # Set CRS (Earth Engine uses EPSG:4326 by default) + gdf.crs = "EPSG:4326" + + gdf = fix_invalid_geometry_in_gdf(gdf) + + # Save as GeoPackage + gdf.to_file(path + ".gpkg", driver="GPKG") + res = push_shape_to_geoserver(path, workspace=workspace, file_type="gpkg") + if style_name: + style_res = geo.publish_style( + layer_name=layer_name, style_name=style_name, workspace=workspace + ) + print("Style response:", style_res) + return res + else: + return "No features in FeatureCollection" + + +def sync_project_fc_to_geoserver(fc, project_name, layer_name, workspace): + print("inside") + print(layer_name) + try: + geojson_fc = fc.getInfo() + except Exception as e: + print("Exception in getInfo()", e) + task_id = sync_vector_to_gcs(fc, layer_name, "GeoJSON") + check_task_status([task_id]) + + geojson_fc = get_geojson_from_gcs(layer_name) + print(len(geojson_fc["features"])) + if len(geojson_fc["features"]) > 0: + state_dir = os.path.join("data/fc_to_shape", project_name) + if not os.path.exists(state_dir): + os.mkdir(state_dir) + path = os.path.join(state_dir, f"{layer_name}") + + # Convert to GeoDataFrame + gdf = gpd.GeoDataFrame.from_features(geojson_fc["features"]) + + # Set CRS (Earth Engine uses EPSG:4326 by default) + gdf.crs = "EPSG:4326" + + gdf = fix_invalid_geometry_in_gdf(gdf) + + # Save as GeoPackage + gdf.to_file(path + ".gpkg", driver="GPKG") + print("pushed to geoserver") + return push_shape_to_geoserver( + path, workspace=workspace, layer_name=layer_name, file_type="gpkg" + ) + else: + print("no features found") + return + + +def to_camelcase(text): + words = text.split() + camelcase = words[0].lower() + for word in words[1:]: + camelcase += word.capitalize() + return camelcase + + +def create_chunk(aoi, description, chunk_size): + size = aoi.size().getInfo() + parts = size // chunk_size + # task_ids = [] + rois = [] + descs = [] + for part in range(parts + 1): + start = part * chunk_size + end = start + chunk_size + block_name_for_parts = description + "_" + str(start) + "-" + str(end) + roi = ee.FeatureCollection(aoi.toList(aoi.size()).slice(start, end)) + if roi.size().getInfo() > 0: + descs.append(block_name_for_parts) + rois.append(roi) + + return rois, descs + + +def merge_chunks( + aoi, + folder_list, + description, + chunk_size, + chunk_asset_path=GEE_HELPER_PATH, + merge_asset_path=GEE_ASSET_PATH, + merge_asset_id=None, +): + print("Merge Chunk task initiated") + ee_initialize() + size = aoi.size().getInfo() + parts = size // chunk_size + assets = [] + for part in range(parts + 1): + start = part * chunk_size + end = start + chunk_size + block_name_for_parts = description + "_" + str(start) + "-" + str(end) + src_asset_id = ( + get_gee_dir_path(folder_list, chunk_asset_path) + block_name_for_parts + ) + if is_gee_asset_exists(src_asset_id): + assets.append(ee.FeatureCollection(src_asset_id)) + + asset = ee.FeatureCollection(assets).flatten() + + asset_id = merge_asset_id or ( + get_gee_dir_path(folder_list, merge_asset_path) + description + ) + try: + # Export an ee.FeatureCollection as an Earth Engine asset. + task = ee.batch.Export.table.toAsset( + **{ + "collection": asset, + "description": description, + "assetId": asset_id, + } + ) + + task.start() + print("Successfully started the merge chunk", task.status()) + return task.status()["id"] + except Exception as e: + print(f"Error occurred in running merge task: {e}") + return None + + +def fix_invalid_geometry_in_gdf(gdf): + invalid = gdf[~gdf.is_valid] + if not invalid.empty: + print("Invalid geometries found:") + for idx, geom in invalid.geometry.items(): + print(f"Index {idx}: {explain_validity(geom)}") + gdf.loc[idx, "geometry"] = gdf.loc[idx, "geometry"].buffer(0) + + return gdf + + +def get_season_key(date): + """Return season key like 'rabi_2017-2018' based on Indian cropping seasons.""" + month = date.month + year = date.year + next_year = year + 1 + + if month in [1, 2]: + return f"rabi_{year - 1}-{year}" # Jan–Feb → Rabi of previous year + elif month in [11, 12]: + return f"rabi_{year}-{next_year}" # Nov–Dec → Rabi starting this year + elif month in [3, 4, 5, 6]: + return f"zaid_{year}-{next_year}" + elif month in [7, 8, 9, 10]: + return f"kharif_{year}-{next_year}" + else: + return None + + +def get_agri_year_key(season_key): + """Convert a season key to agricultural year key (e.g., rabi_2017-2018 → 2017-2018).""" + season, years = season_key.split("_") + start_year, end_year = map(int, years.split("-")) + + if season in ["kharif", "rabi"]: + return f"{start_year}-{end_year}" + elif season == "zaid": + return f"{start_year - 1}-{start_year}" # Zaid 2018-2019 → Agri year 2017-2018 + else: + return None + + +def calculate_precipitation_season( + geojson_filepath, draught_asset_id, start_year=2017, end_year=2024 +): + + # Load the GeoJSON file + with open(geojson_filepath, "r") as f: + feature_collection = json.load(f) + + features_ee = [] + + for feature in feature_collection["features"]: + original_props = feature["properties"] + new_props = {} + + # Copy UID + if "uid" in original_props: + new_props["uid"] = original_props["uid"] + + agri_year_totals = {} + + # Parse precipitation date keys + for key, val in original_props.items(): + try: + date = datetime.strptime(key, "%Y-%m-%d") + season_key = get_season_key(date) + if not season_key: + continue + + agri_key = get_agri_year_key(season_key) + if not agri_key: + continue + + agri_start = int(agri_key.split("-")[0]) + if not (start_year <= agri_start <= end_year): + continue + + season = season_key.split("_")[0] # kharif, rabi etc + full_key = f"{season}_{agri_key}" + + agri_year_totals[full_key] = agri_year_totals.get(full_key, 0) + float( + val + ) + + except Exception: + continue + + # Add all seasonal totals to new_props + for agri_key, total in agri_year_totals.items(): + new_props[f"precipitation_{agri_key}"] = total + + # Create EE Feature + geom_ee = ee.Geometry(feature["geometry"]) + feature_ee = ee.Feature(geom_ee, new_props) + features_ee.append(feature_ee) + + # Left side FC + mws_fc = ee.FeatureCollection(features_ee) + + return mws_fc + + +def generate_geojson_with_ci_and_ndvi(zoi_asset, ci_asset, ndvi_asset, proj_id): + # Load project + proj_obj = Project.objects.get(pk=proj_id) + + # Build CI and NDVI asset paths + asset_path_ci = ( + get_gee_dir_path( + [proj_obj.name], asset_path=GEE_PATHS["WATER_REJ"]["GEE_ASSET_PATH"] + ) + + ci_asset + ) + + asset_path_ndvi = ( + get_gee_dir_path( + [proj_obj.name], asset_path=GEE_PATHS["WATER_REJ"]["GEE_ASSET_PATH"] + ) + + ndvi_asset + ) + + # Load FeatureCollections + zoi = ee.FeatureCollection(zoi_asset) + ci = ee.FeatureCollection(asset_path_ci) + ndvi = ee.FeatureCollection(asset_path_ndvi) + + # ------------------------- + # STEP 1: Join ZOI with Cropping Intensity + # ------------------------- + join = ee.Join.inner() + filter = ee.Filter.intersects(leftField=".geo", rightField=".geo") + zoi_ci_joined = join.apply(zoi, ci, filter) + + def merge_zoi_ci(pair): + zoi_feat = ee.Feature(pair.get("primary")) + ci_feat = ee.Feature(pair.get("secondary")) + merged_props = zoi_feat.toDictionary().combine(ci_feat.toDictionary(), True) + return ee.Feature(zoi_feat.geometry(), merged_props) + + zoi_with_ci = ee.FeatureCollection(zoi_ci_joined.map(merge_zoi_ci)) + + # ------------------------- + # STEP 2: Join ZOI+CI with NDVI + # ------------------------- + zoi_ndvi_joined = join.apply(zoi_with_ci, ndvi, filter) + + def merge_zoi_ci_ndvi(pair): + ci_feat = ee.Feature(pair.get("primary")) + ndvi_feat = ee.Feature(pair.get("secondary")) + merged_props = ci_feat.toDictionary().combine(ndvi_feat.toDictionary(), True) + return ee.Feature(ci_feat.geometry(), merged_props) + + final_merged = ee.FeatureCollection(zoi_ndvi_joined.map(merge_zoi_ci_ndvi)) + + # ------------------------- + # STEP 3: Export or Push to GeoServer + # ------------------------- + layer_name = f"WaterRejapp_zoi_{proj_obj.name}_{proj_obj.id}" + sync_project_fc_to_geoserver(final_merged, proj_obj.name, layer_name, "waterrej") + + +def get_directory_size(path): + total_size = 0 + for dirpath, dirnames, filenames in os.walk(path): + for filename in filenames: + file_path = os.path.join(dirpath, filename) + if os.path.isfile(file_path): + total_size += os.path.getsize(file_path) + return total_size + + +def generate_geojson_with_ci_ndvi_ndmi( + zoi_asset, ci_asset, ndvi_asset, ndmi_asset, proj_id +): + + # Load project + proj_obj = Project.objects.get(pk=proj_id) + + zoi = ee.FeatureCollection(zoi_asset) + print("Number of features zoi:", zoi.size().getInfo()) + + ci = ee.FeatureCollection(ci_asset) + print("Number of features zoi:", ci.size().getInfo()) + ndvi = ee.FeatureCollection(ndmi_asset) + print("Number of features zoi:", ndvi.size().getInfo()) + ndmi = ee.FeatureCollection(ndmi_asset) + print("Number of features zoi:", ndmi.size().getInfo()) + + # ------------------------- + # STEP 1: Join ZOI with CI + # ------------------------- + join = ee.Join.inner() + filter = ee.Filter.intersects(leftField=".geo", rightField=".geo") + zoi_ci_joined = join.apply(zoi, ci, filter) + + def merge_zoi_ci(pair): + zoi_feat = ee.Feature(pair.get("primary")) + ci_feat = ee.Feature(pair.get("secondary")) + merged_props = zoi_feat.toDictionary().combine(ci_feat.toDictionary(), True) + return ee.Feature(zoi_feat.geometry(), merged_props) # ✅ keep ZOI geom + + zoi_with_ci = ee.FeatureCollection(zoi_ci_joined.map(merge_zoi_ci)) + + # ------------------------- + # STEP 2: Join with NDVI + # ------------------------- + zoi_ndvi_joined = join.apply(zoi_with_ci, ndvi, filter) + + def merge_zoi_ci_ndvi(pair): + prev_feat = ee.Feature(pair.get("primary")) + ndvi_feat = ee.Feature(pair.get("secondary")) + merged_props = prev_feat.toDictionary().combine(ndvi_feat.toDictionary(), True) + return ee.Feature(prev_feat.geometry(), merged_props) # ✅ still ZOI geom + + zoi_ci_ndvi = ee.FeatureCollection(zoi_ndvi_joined.map(merge_zoi_ci_ndvi)) + + # ------------------------- + # STEP 3: Join with NDMI + # ------------------------- + zoi_ndmi_joined = join.apply(zoi_ci_ndvi, ndmi, filter) + + def merge_zoi_ci_ndvi_ndmi(pair): + prev_feat = ee.Feature(pair.get("primary")) + ndmi_feat = ee.Feature(pair.get("secondary")) + merged_props = prev_feat.toDictionary().combine(ndmi_feat.toDictionary(), True) + return ee.Feature(prev_feat.geometry(), merged_props) # ✅ keep ZOI geom + + final_merged = ee.FeatureCollection(zoi_ndmi_joined.map(merge_zoi_ci_ndvi_ndmi)) + + # ------------------------- + # STEP 4: Export or Push to GeoServer + # ------------------------- + layer_name = f"WaterRejapp_zoi_{proj_obj.name}_{proj_obj.id}" + print(layer_name) + sync_project_fc_to_geoserver(final_merged, proj_obj.name, layer_name, "waterrej") + + +def generate_geojson_with_ci_ndvi(zoi_asset, ci_asset, ndvi_asset, proj_id): + # Load project + proj_obj = Project.objects.get(pk=proj_id) + + # Initialize Earth Engine + ee_initialize(4) + + # Load FeatureCollections + zoi = ee.FeatureCollection(zoi_asset) + ci = ee.FeatureCollection(ci_asset) + ndvi = ee.FeatureCollection(ndvi_asset) + + print("ZOI:", zoi.size().getInfo()) + print("CI:", ci.size().getInfo()) + print("NDVI:", ndvi.size().getInfo()) + + # Common join logic on UID + join = ee.Join.inner() + uid_filter = ee.Filter.equals(leftField="UID", rightField="UID") + + # --- Join ZOI + CI --- + zoi_ci_joined = join.apply(zoi, ci, uid_filter) + + def merge_zoi_ci(pair): + zoi_feat = ee.Feature(pair.get("primary")) + ci_feat = ee.Feature(pair.get("secondary")) + merged_props = zoi_feat.toDictionary().combine(ci_feat.toDictionary(), True) + # Keep ZOI geometry only + return ee.Feature(zoi_feat.geometry(), merged_props) + + zoi_with_ci = ee.FeatureCollection(zoi_ci_joined.map(merge_zoi_ci)) + + # --- Join with NDVI --- + zoi_ndvi_joined = join.apply(zoi_with_ci, ndvi, uid_filter) + + def merge_with_ndvi(pair): + base_feat = ee.Feature(pair.get("primary")) + ndvi_feat = ee.Feature(pair.get("secondary")) + merged_props = base_feat.toDictionary().combine(ndvi_feat.toDictionary(), True) + # Always retain ZOI geometry + return ee.Feature(base_feat.geometry(), merged_props) + + merged_final = ee.FeatureCollection(zoi_ndvi_joined.map(merge_with_ndvi)) + + # --- Ensure ZOI geometry retained in all features --- + merged_final = merged_final.map( + lambda f: ee.Feature( + f.setGeometry( + ee.Feature( + zoi.filter(ee.Filter.eq("UID", f.get("UID"))).first() + ).geometry() + ) + ) + ) + + layer_name = f"WaterRejapp_zoi_{proj_obj.name}_{proj_obj.id}" + print(layer_name) + + sync_project_fc_to_geoserver(merged_final, proj_obj.name, layer_name, "waterrej") + + +def save_layer_info_to_db( + state, + district, + block, + layer_name, + asset_id, + dataset_name, + sync_to_geoserver=False, + layer_version="1.0", + algorithm=None, + algorithm_version="1.0", + misc=None, + is_override=False, +): + print("inside the save_layer_info_to_db function") + + dataset = Dataset.objects.get(name=dataset_name) + + try: + state_obj = StateSOI.objects.get(state_name__iexact=state) + district_obj = DistrictSOI.objects.get( + district_name__iexact=district, state=state_obj + ) + block_obj = TehsilSOI.objects.get( + tehsil_name__iexact=block, district=district_obj + ) + except Exception as e: + print("Error fetching in state district block:", e) + return + + is_public = is_asset_public(asset_id) + + # Check if there’s an existing layer + existing_layer = ( + Layer.objects.filter( + dataset=dataset, + layer_name=layer_name, + state=state_obj, + district=district_obj, + block=block_obj, + ) + .order_by("-layer_version") + .first() + ) + + if existing_layer: + if existing_layer.algorithm_version != algorithm_version: + # Algorithm version changed --> create new record with incremented layer_version + new_layer_version = str(float(existing_layer.layer_version) + 1) + print( + f"Algorithm version changed. Creating new layer version: {new_layer_version}" + ) + layer_obj = Layer.objects.create( + dataset=dataset, + layer_name=layer_name, + state=state_obj, + district=district_obj, + block=block_obj, + layer_version=new_layer_version, + algorithm=algorithm, + algorithm_version=algorithm_version, + is_sync_to_geoserver=sync_to_geoserver, + is_public_gee_asset=is_public, + is_override=is_override, + misc=misc, + gee_asset_path=asset_id, + ) + else: + # Algorithm version is same --> update existing layer + print("Algorithm version same. Updating existing layer.") + for field, value in { + "algorithm": algorithm, + "algorithm_version": algorithm_version, + "is_sync_to_geoserver": sync_to_geoserver, + "is_public_gee_asset": is_public, + "is_override": is_override, + "misc": misc, + "gee_asset_path": asset_id, + }.items(): + setattr(existing_layer, field, value) + existing_layer.save() + layer_obj = existing_layer + else: + # No existing record --> create a new one + print("No existing layer found. Creating new one.") + layer_obj = Layer.objects.create( + dataset=dataset, + layer_name=layer_name, + state=state_obj, + district=district_obj, + block=block_obj, + layer_version=layer_version, + algorithm=algorithm, + algorithm_version=algorithm_version, + is_sync_to_geoserver=sync_to_geoserver, + is_public_gee_asset=is_public, + is_override=is_override, + misc=misc, + gee_asset_path=asset_id, + ) + + print(f"Saved layer info (id={layer_obj.id}, version={layer_obj.layer_version})") + return layer_obj.id + + +def get_existing_end_year(dataset_name, layer_name): + """fetch objects from db on the basis of dataset name and layer_name""" + dataset = Dataset.objects.get(name=dataset_name) + layer_obj = Layer.objects.get(dataset=dataset, layer_name=layer_name) + existing_end_date = layer_obj.misc["end_year"] + print("existing_end_date", existing_end_date) + return existing_end_date + + +def get_layer_object(state, district, block, layer_name, dataset_name): + state_obj = StateSOI.objects.get(state_name__iexact=state) + district_obj = DistrictSOI.objects.get( + district_name__iexact=district, state=state_obj + ) + block_obj = TehsilSOI.objects.get(tehsil_name__iexact=block, district=district_obj) + layer_obj = ( + Layer.objects.filter( + state=state_obj, + district=district_obj, + block=block_obj, + layer_name=layer_name, + dataset__name=dataset_name, + ) + .order_by("-layer_version") + .first() + ) + return layer_obj + + +def update_dashboard_geojson( + state=None, + district=None, + block=None, + layer_name=None, + workspace_name=None, + proj_id=None, +): + if state and block and block: + print(f"🔄 Updating GeoJSON for {state}, {district}, {block}") + + # Get related objects + state_obj = StateSOI.objects.get(state_name=state) + district_obj = DistrictSOI.objects.get(district_name=district) + tehsil_obj = TehsilSOI.objects.get(tehsil_name=block) # fixed typo + + # Get or create main record + obj, created = State_Disritct_Block_Properties.objects.get_or_create( + state=state_obj, district=district_obj, tehsil=tehsil_obj + ) + else: + obj = Project.objects.get(pk=proj_id) + + # Map suffix to json_key + suffix_to_key = { + "wb": "wb_geojson", + "zoi": "zoi_geojson", + "mws": "mws_geojson", + } + + # Detect which key this layer corresponds to + json_key = None + for suffix, key in suffix_to_key.items(): + if layer_name == f"{state}_{district}_{block}_{suffix}": + json_key = key + break + + if not json_key: + print(f"⚠️ Layer name {layer_name} did not match any known type.") + return + + # Construct GeoServer URL + waterrej_url = ( + f"https://geoserver.core-stack.org:8443/geoserver/waterrej/ows?" + f"service=WFS&version=1.0.0&request=GetFeature&typeName={workspace_name}:{layer_name}" + f"&outputFormat=application%2Fjson" + ) + + # Load existing dashboard_geojson or create new + if proj_id: + misc = obj.dashboard_geojson or {} + else: + misc = obj.geojson_path or {} + + # Ensure waterrej section exists + if "waterrej" not in misc: + misc["waterrej"] = {} + + # Update or add this specific json_key + misc["waterrej"][json_key] = waterrej_url + + # Save the updated JSON field + obj.dashboard_geojson = misc + obj.save() + + print(f"✅ Added/Updated {json_key} for {state}, {district}, {block}") + + +def clean_geometry(geom): + """ + Clean geometry: + - Dissolve multipolygon → single polygon + - Remove holes automatically + - Fix invalid topology + - Buffer tiny polygons + """ + + # 1. Dissolve multi-polygons and remove holes + geom = geom.dissolve(maxError=1) + + # 2. Fix invalid rings by simplifying slightly (NEVER buffer(0)) + geom = geom.simplify(1) + + # 3. Buffer polygons smaller than 1 pixel (< 900 m²) + area = geom.area() + geom = ee.Algorithms.If( + area.lt(900), + geom.buffer(15), + geom, # ensure raster pixel center is captured + ) + + return ee.Geometry(geom) + + +def safe_reduce_max(image, geom, scale=30): + geom = clean_geometry(geom) + + val = ( + image.unmask(0) + .reduceRegion( + reducer=ee.Reducer.max(), + geometry=geom, + scale=scale, + maxPixels=1e13, + tileScale=4, + bestEffort=True, + ) + .get("b1") + ) + + return ee.Number(ee.Algorithms.If(val, val, 0)) + + +# ------------------------------------------------------ +# SAFE REDUCE MAX FUNCTION +# ------------------------------------------------------ +def safe_reduce_max(image, geom, scale=30): + geom = clean_geometry(geom) + + result = ( + image.unmask(0) + .reduceRegion( + reducer=ee.Reducer.max(), + geometry=geom, + scale=scale, + maxPixels=1e13, + tileScale=4, + bestEffort=True, + ) + .get("b1") + ) + + # Convert null → 0 + return ee.Number(ee.Algorithms.If(result, result, 0)) + + +# ------------------------------------------------------ +# MAIN FUNCTION TO PROCESS SWB LAYER +# ------------------------------------------------------ +def generate_swb_layer_with_max_so_catchment( + roi=None, + app_type="MWS", + asset_suffix=None, + asset_folder=None, + gee_account_id=None, +): + ee_initialize(gee_account_id) + + # Build asset paths + base_path = get_gee_dir_path( + asset_folder, asset_path=GEE_PATHS[app_type]["GEE_ASSET_PATH"] + ) + + so_asset = f"{base_path}stream_order_{asset_suffix}_raster" + ca_asset = f"{base_path}catchment_area_{asset_suffix}_raster" + + # Load rasters + stream_order_band = ee.Image(so_asset).select("b1") + catchment_band = ee.Image(ca_asset).select("b1") + + # Processing per waterbody + def compute_for_feature(feature): + geom = feature.geometry() + + max_so = safe_reduce_max(stream_order_band, geom, scale=30) + max_ca = safe_reduce_max(catchment_band, geom, scale=30) + + return feature.set( + { + "max_stream_order": max_so, + "max_catchment_area": max_ca, + } + ) + + # Map over the feature collection + return roi.map(compute_for_feature) + + +def _get_prod_backend_url(): + return getattr(settings, "PROD_BACKEND_URL", "").rstrip("/") + + +def _get_prod_api_key(): + return getattr(settings, "PROD_BACKEND_API_KEY", "") + + +def _sync_layer_to_prod_db(payload: dict): + prod_url = _get_prod_backend_url() + if not prod_url: + return None + + endpoint = prod_url + "/api/v1/sync_layer_remote/" + try: + response = requests.post( + endpoint, + json=payload, + headers={"X-Api-Key": _get_prod_api_key()}, + timeout=30, + ) + if response.status_code not in (200, 201): + logger.warning( + "Prod DB sync returned %s for layer %s: %s", + response.status_code, + payload.get("layer_name"), + response.text, + ) + return None + layer_id = response.json().get("layer_id") + logger.info( + "Layer %s synced to prod DB (id=%s).", payload.get("layer_name"), layer_id + ) + return layer_id + except requests.RequestException as e: + logger.error( + "Failed to sync layer %s to prod DB: %s", payload.get("layer_name"), e + ) + return None + + +def _update_layer_sync_remote( + layer_id, sync_to_geoserver=None, is_stac_specs_generated=None +): + prod_url = _get_prod_backend_url() + if not prod_url or layer_id is None: + return + + endpoint = prod_url + "/api/v1/update_layer_sync_remote/" + payload = { + "layer_id": layer_id, + "sync_to_geoserver": sync_to_geoserver, + "is_stac_specs_generated": is_stac_specs_generated, + } + try: + response = requests.post( + endpoint, + json=payload, + headers={"X-Api-Key": _get_prod_api_key()}, + timeout=30, + ) + if response.status_code not in (200, 201): + logger.warning( + "Prod layer sync status update returned %s for layer %s: %s", + response.status_code, + layer_id, + response.text, + ) + else: + logger.info("Layer sync status updated on prod DB for id=%s.", layer_id) + except requests.RequestException as e: + logger.error( + "Failed to update layer sync status on prod DB for id=%s: %s", layer_id, e + ) + + +def update_layer_sync_status( + layer_id, sync_to_geoserver=None, is_stac_specs_generated=None +): + if _get_prod_backend_url(): + _update_layer_sync_remote( + layer_id, + sync_to_geoserver=sync_to_geoserver, + is_stac_specs_generated=is_stac_specs_generated, + ) + return layer_id + + try: + layer_obj = Layer.objects.filter(id=layer_id).first() + if layer_obj is None: + return None + + update_fields = [] + if sync_to_geoserver is not None: + layer_obj.is_sync_to_geoserver = sync_to_geoserver + update_fields.append("is_sync_to_geoserver") + if is_stac_specs_generated is not None: + layer_obj.is_stac_specs_generated = is_stac_specs_generated + update_fields.append("is_stac_specs_generated") + + # `save(update_fields=...)` fires the post_save signal so the STAC + # auto-trigger handler in `computing.signals` can pick up the flip. + if update_fields: + layer_obj.save(update_fields=update_fields) + print( + f"Updated {update_fields} for layer ID: {layer_id} " + f"(sync={sync_to_geoserver}, stac={is_stac_specs_generated})" + ) + return layer_id + + except Exception as e: + print(f"Error updating layer sync status: {e}") + + +# send missing layer to recipient email +def send_missing_layers_report(result: dict, recipients: list = None) -> bool: + if recipients is None: + recipients = getattr(settings, "MISSING_LAYER_RECIPIENTS", []) + + if isinstance(recipients, str): + recipients = [recipients] + + if not recipients: + logger.error("No recipients configured for missing layers report.") + return False + + summary = [] + total_missing = 0 + + for layer, data in result.items(): + count = len(data.get("missing_layers", [])) + total_missing += count + summary.append(f"{layer}: {count}") + body = ( + "Missing Layers Report\n\n" + f"Total Missing: {total_missing}\n\n" + + "\n".join(summary) + + "\n\nDetailed report attached." + ) + + attachment_content = json.dumps(result, indent=4) + max_retries = 3 + + for attempt in range(max_retries): + connection = None + try: + connection = get_connection(timeout=120) + connection.open() + email = EmailMessage( + subject="Missing Layers Report", + body=body, + from_email=settings.EMAIL_HOST_USER, + to=recipients, + connection=connection, + ) + email.attach( + "missing_layers.json", + attachment_content, + "application/json", + ) + email.send() + logger.info(f"Missing layers report sent to {recipients}") + logger.info( + f"Attachment size: " + f"{len(attachment_content.encode('utf-8')) / 1024:.2f} KB" + ) + return True + except Exception as e: + logger.exception(f"Attempt {attempt + 1}/{max_retries} failed: {e}") + if attempt < max_retries - 1: + wait_time = 5 * (attempt + 1) + logger.info(f"Retrying after {wait_time} seconds...") + time.sleep(wait_time) + else: + logger.error("All attempts to send email failed.") + return False + finally: + if connection: + try: + connection.close() + except Exception: + pass + + +def _is_cache_valid(cache: dict, workspace: str) -> bool: + if workspace not in cache: + return False + age = time.time() - cache[workspace]["cached_at"] + if age > 3600: + logger.info(f"Cache expired for {workspace} (age: {int(age)}s)") + return False + return True + + +def _set_cache(cache: dict, workspace: str, data: set): + cache[workspace] = { + "data": data, + "cached_at": time.time(), + } diff --git a/computing/views.py b/computing/views.py index 88dd6982..3f3595c8 100644 --- a/computing/views.py +++ b/computing/views.py @@ -1,329 +1,479 @@ -import requests -from nrm_app.settings import GEOSERVER_URL -from utilities.gee_utils import valid_gee_text -import xml.etree.ElementTree as ET -from lxml import etree as LET -from nrm_app.celery import app -from computing.models import * -from utilities.geoserver_utils import Geoserver -import json -import os -from django.conf import settings -from pathlib import Path -from utilities.constants import GEOSERVER_BASE - - -def get_url(geoserver_url, workspace, layer_name): - return ( - f"{geoserver_url}/{workspace}/ows" - f"?service=WFS" - f"&version=1.1.0" - f"&request=GetFeature" - f"&typeName={workspace}:{layer_name}" - f"&resultType=hits" - ) - - -def load_workspace_config(): - """ - Load workspace configuration from JSON file. - """ - config_path = ( - Path(settings.BASE_DIR) - / "data" - / "layers" - / "layer_status" - / "layer_mapping.json" - ) - with open(config_path, "r") as f: - return json.load(f) - - -@app.task(bind=True) -def layer_status(self, state, district, block): - """ - Check the status of all layers for a particular location. - - Args: - self: Instance reference - state: State name - district: District name - block: Block name - - Returns: - Dictionary with status of all workspace layers - """ - print(f"{state=}") - all_workspace_statuses = {} - capabilities_cache = {} - district = valid_gee_text(district.lower()) - block = valid_gee_text(block.lower()) - - # Load workspace configuration from JSON - workspace_config = load_workspace_config() - - for workspace_display, config in workspace_config.items(): - workspace = config.get("name") - suffix = config.get("suffix", "") - prefix = config.get("prefix", "") - layer_type = config.get("type", "") - - # constructing layer name - layer_name_parts = [prefix, district, block, suffix] - layer_name = "_".join(part for part in layer_name_parts if part) - - total_features = 0 - end_date = None - start_date = None - status_code = 400 - # checking for vector layer - if layer_type == "vector": - layer_url = get_url(GEOSERVER_URL, workspace, layer_name) - res_layer_url = requests.get(layer_url) - - if res_layer_url.status_code == 200: - try: - root = ET.fromstring(res_layer_url.text) - # Extract feature count from WFS hits response - total_features = int(root.attrib.get("numberOfFeatures", 0)) - status_code = 200 if total_features > 0 else 400 - layer = ( - Layer.objects.filter(layer_name=layer_name) - .order_by("-layer_version") - .first() - ) - if layer and layer.misc: - start_date = layer.misc.get("start_date") - end_date = layer.misc.get("end_date") - - except ET.ParseError: - print(f"Invalid XML for layer: {layer_name}") - status_code = 400 - else: - if workspace not in capabilities_cache: - capabilities_url = f"{GEOSERVER_BASE}{workspace}/wms?service=WMS&request=GetCapabilities" - try: - response = requests.get(capabilities_url, timeout=30) - if response.status_code == 200: - parser = LET.XMLParser(recover=True, encoding="utf-8") - root = LET.fromstring(response.content, parser=parser) - ns = {"wms": root.tag.split("}")[0].strip("{")} - layers = root.findall(".//wms:Layer/wms:Name", namespaces=ns) - capabilities_cache[workspace] = { - layer.text for layer in layers if layer.text - } - else: - capabilities_cache[workspace] = set() - except Exception as e: - print( - f"Failed to fetch capabilities for workspace {workspace}: {e}" - ) - capabilities_cache[workspace] = set() - - if layer_name in capabilities_cache.get(workspace, set()): - status_code = 200 - all_workspace_statuses[workspace_display] = { - "workspace": workspace, - "layer_name": layer_name, - "status_code": status_code, - "totalFeature": total_features, - "endDate": end_date, - "startDate": start_date, - } - - return all_workspace_statuses - - -def load_workspace_types(): - """ - Load workspace types configuration from JSON file. - """ - config_path = ( - Path(settings.BASE_DIR) - / "data" - / "layers" - / "workspace_layers" - / "layers_in_workspace.json" - ) - with open(config_path, "r") as f: - return json.load(f) - - -@app.task(bind=True) -def get_layers_of_workspace(self, workspace): - """ - It will take workspace as argument and returns all the layers which is present on geoserver. - """ - # Load workspace types from JSON - workspace_types = load_workspace_types() - raster_workspace = workspace_types["raster_workspace"] - vector_workspace = workspace_types["vector_workspace"] - raster_and_vector_workspace = workspace_types["raster_and_vector_workspace"] - - geo = Geoserver() - layers = geo.get_layers(workspace) - layer_names = [layer["name"] for layer in layers["layers"]["layer"]] - if workspace in raster_workspace: - print("you passed raster workspace") - available_layers = valid_raster_layers_for_workspace(workspace) - valid_layers = [ln for ln in layer_names if ln in available_layers] - invalid_layers = [ln for ln in layer_names if ln not in available_layers] - return {"valid_layer": valid_layers, "invalid_layers": invalid_layers} - elif workspace in vector_workspace: - print("you passed vector workspace") - valid_layers = [] - invalid_layers = [] - for layer_name in layer_names: - if is_valid_vector_layer(workspace, layer_name): - valid_layers.append(layer_name) - else: - invalid_layers.append(layer_name) - return {"valid_layer": valid_layers, "invalid_layers": invalid_layers} - elif workspace in raster_and_vector_workspace: - print("you passed workspace which contain both layers(raster and vector)") - valid_layers = [] - invalid_layers = [] - for layer_name in layer_names: - if "vector" in layer_name.lower(): - if is_valid_vector_layer(workspace, layer_name): - valid_layers.append(layer_name) - else: - invalid_layers.append(layer_name) - elif "raster" in layer_name.lower(): - available_layers = valid_raster_layers_for_workspace(workspace) - if layer_name in available_layers: - valid_layers.append(layer_name) - else: - invalid_layers.append(layer_name) - return {"valid_layer": valid_layers, "invalid_layers": invalid_layers} - else: - print("you passed wrong workspace") - return [] - - -def check_missing_layers(workspace): - missing_layers = [] - print(f"{workspace=}") - workspace_config = load_workspace_config() - workspace_types = get_workspace_types(workspace) - print(f"Found types: {workspace_types}") - if not workspace_types: - print(f"No config found for workspace: {workspace}") - return {"no config found": []} - layer_config = get_layer_config_by_type( - workspace_config, workspace, workspace_types - ) - # fetch raster layers once - available_raster_layers = valid_raster_layers_for_workspace(workspace) - count = 0 - active_tehsils = TehsilSOI.objects.filter(active_status=True) - for tehsil in active_tehsils: - state = tehsil.district.state.state_name - district = tehsil.district.district_name - tehsil = tehsil.tehsil_name - district_name = valid_gee_text(district.lower()) - tehsil_name = valid_gee_text(tehsil.lower()) - for layer_type, config in layer_config.items(): - prefix = config.get("prefix") - suffix = config.get("suffix") - layer_name_parts = [ - prefix, - district_name, - tehsil_name, - suffix, - ] - layer_name = "_".join(part for part in layer_name_parts if part) - # raster check - if layer_type == "raster": - count += 1 - if layer_name not in available_raster_layers: - missing_layers.append(state + " " + layer_name) - - # vector check - elif layer_type == "vector": - count += 1 - is_valid = is_valid_vector_layer(workspace, layer_name) - - if not is_valid: - missing_layers.append(state + " " + layer_name) - - return {"missing_layers": missing_layers, "layer_count": count} - - -def valid_raster_layers_for_workspace(workspace): - """ - - Args: - workspace: - - Returns: - all valid (have data)layers for particular workspace - """ - capabilities_url = ( - f"{GEOSERVER_BASE}{workspace}/wms?service=WMS&request=GetCapabilities" - ) - response = requests.get(capabilities_url) - if response.status_code != 200: - print(f"Failed to retrieve capabilities from {workspace}.") - return [] - root = ET.fromstring(response.content) - ns = {"wms": root.tag.split("}")[0].strip("{")} - layers = root.findall(".//wms:Layer/wms:Name", namespaces=ns) - available_layers = [layer.text for layer in layers] - return available_layers - - -def is_valid_vector_layer(workspace, layer_name): - """ - - Args: - workspace: - layer_name: - - Returns: - True if layer have data else False - """ - layer_url = get_url(GEOSERVER_URL, workspace, layer_name) - res_layer_url = requests.get(layer_url) - if res_layer_url.status_code == 200: - root = ET.fromstring(res_layer_url.text) - total_features = int(root.attrib.get("numberOfFeatures", 0)) - return True if total_features > 0 else False - - -def get_workspace_types(workspace_name): - """ - Return all layer types for a given workspace name from DB. - Example: ['raster', 'vector'] - """ - return list( - Dataset.objects.filter(workspace=workspace_name) - .values_list("layer_type", flat=True) - .distinct() - ) - - -def get_layer_config_by_type(workspace_config, workspace_name, layer_types): - """ - Return prefix and suffix for each layer type. - - Args: - workspace_config (dict): config JSON - workspace_name (str): dataset name - layer_types (list): ['raster', 'vector'] - - Returns: - dict - """ - result = {} - - for config in workspace_config.values(): - if config.get("name") == workspace_name and config.get("type") in layer_types: - layer_type = config["type"] - - result[layer_type] = { - "prefix": config.get("prefix"), - "suffix": config.get("suffix"), - } - - return result +import requests +from nrm_app.settings import GEOSERVER_URL +from utilities.gee_utils import valid_gee_text +import xml.etree.ElementTree as ET +from lxml import etree as LET +from nrm_app.celery import app +from computing.models import * +from utilities.geoserver_utils import Geoserver +import json +from django.conf import settings +from pathlib import Path +from utilities.constants import GEOSERVER_BASE +from utilities.logger import setup_logger +from concurrent.futures import ThreadPoolExecutor, as_completed +from requests.adapters import HTTPAdapter +from urllib3.util.retry import Retry +from rest_framework.response import Response +from rest_framework import status +from .utils import send_missing_layers_report, _is_cache_valid, _set_cache + +logger = setup_logger(__name__) + + +def get_url(geoserver_url, workspace, layer_name): + return ( + f"{geoserver_url}/{workspace}/ows" + f"?service=WFS" + f"&version=1.1.0" + f"&request=GetFeature" + f"&typeName={workspace}:{layer_name}" + f"&resultType=hits" + ) + + +def load_workspace_config(): + """ + Load workspace configuration from JSON file. + """ + config_path = ( + Path(settings.BASE_DIR) + / "data" + / "layers" + / "layer_status" + / "layer_mapping.json" + ) + with open(config_path, "r") as f: + return json.load(f) + + +@app.task(bind=True) +def layer_status(self, state, district, block): + """ + Check the status of all layers for a particular location. + + Args: + self: Instance reference + state: State name + district: District name + block: Block name + + Returns: + Dictionary with status of all workspace layers + """ + print(f"{state=}") + all_workspace_statuses = {} + capabilities_cache = {} + district = valid_gee_text(district.lower()) + block = valid_gee_text(block.lower()) + + # Load workspace configuration from JSON + workspace_config = load_workspace_config() + + for workspace_display, config in workspace_config.items(): + workspace = config.get("name") + suffix = config.get("suffix", "") + prefix = config.get("prefix", "") + layer_type = config.get("type", "") + + # constructing layer name + layer_name_parts = [prefix, district, block, suffix] + layer_name = "_".join(part for part in layer_name_parts if part) + + total_features = 0 + end_date = None + start_date = None + status_code = 400 + # checking for vector layer + if layer_type == "vector": + layer_url = get_url(GEOSERVER_URL, workspace, layer_name) + res_layer_url = requests.get(layer_url) + + if res_layer_url.status_code == 200: + try: + root = ET.fromstring(res_layer_url.text) + # Extract feature count from WFS hits response + total_features = int(root.attrib.get("numberOfFeatures", 0)) + status_code = 200 if total_features > 0 else 400 + layer = ( + Layer.objects.filter(layer_name=layer_name) + .order_by("-layer_version") + .first() + ) + if layer and layer.misc: + start_date = layer.misc.get("start_date") + end_date = layer.misc.get("end_date") + + except ET.ParseError: + print(f"Invalid XML for layer: {layer_name}") + status_code = 400 + else: + if workspace not in capabilities_cache: + capabilities_url = f"{GEOSERVER_BASE}{workspace}/wms?service=WMS&request=GetCapabilities" + try: + response = requests.get(capabilities_url, timeout=30) + if response.status_code == 200: + parser = LET.XMLParser(recover=True, encoding="utf-8") + root = LET.fromstring(response.content, parser=parser) + ns = {"wms": root.tag.split("}")[0].strip("{")} + layers = root.findall(".//wms:Layer/wms:Name", namespaces=ns) + capabilities_cache[workspace] = { + layer.text for layer in layers if layer.text + } + else: + capabilities_cache[workspace] = set() + except Exception as e: + print( + f"Failed to fetch capabilities for workspace {workspace}: {e}" + ) + capabilities_cache[workspace] = set() + + if layer_name in capabilities_cache.get(workspace, set()): + status_code = 200 + all_workspace_statuses[workspace_display] = { + "workspace": workspace, + "layer_name": layer_name, + "status_code": status_code, + "totalFeature": total_features, + "endDate": end_date, + "startDate": start_date, + } + + return all_workspace_statuses + + +def load_workspace_types(): + """ + Load workspace types configuration from JSON file. + """ + config_path = ( + Path(settings.BASE_DIR) + / "data" + / "layers" + / "workspace_layers" + / "layers_in_workspace.json" + ) + with open(config_path, "r") as f: + return json.load(f) + + +@app.task(bind=True) +def get_layers_of_workspace(self, workspace): + """ + It will take workspace as argument and returns all the layers which is present on geoserver. + """ + # Load workspace types from JSON + workspace_types = load_workspace_types() + raster_workspace = workspace_types["raster_workspace"] + vector_workspace = workspace_types["vector_workspace"] + raster_and_vector_workspace = workspace_types["raster_and_vector_workspace"] + + geo = Geoserver() + layers = geo.get_layers(workspace) + layer_names = [layer["name"] for layer in layers["layers"]["layer"]] + if workspace in raster_workspace: + print("you passed raster workspace") + available_layers = valid_raster_layers_for_workspace(workspace) + valid_layers = [ln for ln in layer_names if ln in available_layers] + invalid_layers = [ln for ln in layer_names if ln not in available_layers] + return {"valid_layer": valid_layers, "invalid_layers": invalid_layers} + elif workspace in vector_workspace: + print("you passed vector workspace") + valid_layers = [] + invalid_layers = [] + for layer_name in layer_names: + if is_valid_vector_layer(workspace, layer_name): + valid_layers.append(layer_name) + else: + invalid_layers.append(layer_name) + return {"valid_layer": valid_layers, "invalid_layers": invalid_layers} + elif workspace in raster_and_vector_workspace: + print("you passed workspace which contain both layers(raster and vector)") + valid_layers = [] + invalid_layers = [] + for layer_name in layer_names: + if "vector" in layer_name.lower(): + if is_valid_vector_layer(workspace, layer_name): + valid_layers.append(layer_name) + else: + invalid_layers.append(layer_name) + elif "raster" in layer_name.lower(): + available_layers = valid_raster_layers_for_workspace(workspace) + if layer_name in available_layers: + valid_layers.append(layer_name) + else: + invalid_layers.append(layer_name) + return {"valid_layer": valid_layers, "invalid_layers": invalid_layers} + else: + print("you passed wrong workspace") + return [] + + +_raster_cache = {} +_vector_cache = {} + + +def valid_raster_layers_for_workspace(workspace: str) -> set: + if _is_cache_valid(_raster_cache, workspace): + logger.info(f"Cache hit for raster: {workspace}") + return _raster_cache[workspace]["data"] + + session = get_session_with_retry() + capabilities_url = ( + f"{GEOSERVER_BASE}{workspace}/wms?service=WMS&request=GetCapabilities" + ) + try: + response = session.get(capabilities_url, timeout=(5, 15)) + response.raise_for_status() + except requests.exceptions.Timeout: + logger.warning( + f"Timeout fetching raster capabilities for {workspace} — not caching" + ) + return set() + except requests.exceptions.RequestException as e: + logger.error(f"Failed raster capabilities for {workspace}: {e} — not caching") + return set() + + root = ET.fromstring(response.content) + ns = {"wms": root.tag.split("}")[0].strip("{")} + layers = root.findall(".//wms:Layer/wms:Name", namespaces=ns) + result = {layer.text for layer in layers} + + _set_cache(_raster_cache, workspace, result) # cache with timestamp + logger.info(f"Cached raster layers for {workspace}: {len(result)} layers") + return result + + +def valid_vector_layers_for_workspace(workspace: str) -> set: + if _is_cache_valid(_vector_cache, workspace): + logger.info(f"Cache hit for vector: {workspace}") + return _vector_cache[workspace]["data"] + + session = get_session_with_retry() + capabilities_url = ( + f"{GEOSERVER_BASE}{workspace}/wfs" + f"?service=WFS&version=2.0.0&request=GetCapabilities" + ) + try: + response = session.get(capabilities_url, timeout=(5, 15)) + response.raise_for_status() + except requests.exceptions.Timeout: + logger.warning( + f"Timeout fetching vector capabilities for {workspace} — not caching" + ) + return set() + except requests.exceptions.RequestException as e: + logger.error(f"Failed vector capabilities for {workspace}: {e} — not caching") + return set() + + root = ET.fromstring(response.content) + ns = {"wfs": "http://www.opengis.net/wfs/2.0"} + names = root.findall(".//wfs:FeatureType/wfs:Name", namespaces=ns) + result = {name.text.split(":")[-1] for name in names} + + _set_cache(_vector_cache, workspace, result) # cache with timestamp + logger.info(f"Cached vector layers for {workspace}: {len(result)} layers") + return result + + +def is_valid_vector_layer(workspace: str, layer_name: str) -> bool: + """Used in parallel fallback only.""" + session = get_session_with_retry() + try: + layer_url = get_url(GEOSERVER_URL, workspace, layer_name) + res = session.get(layer_url, timeout=(5, 10)) # shorter timeout per layer + if res.status_code == 200: + root = ET.fromstring(res.text) + return int(root.attrib.get("numberOfFeatures", 0)) > 0 + except requests.exceptions.Timeout: + logger.warning(f"Timeout checking vector layer: {layer_name}") + except Exception as e: + logger.warning(f"Vector check failed for {layer_name}: {e}") + return False + + +def bulk_check_vector_layers( + workspace: str, + layer_names: list[str], + max_workers: int = 20, +) -> dict[str, bool]: + """ + Checks multiple vector layers concurrently using a thread pool. + Returns {layer_name: is_valid} mapping. + """ + results = {} + with ThreadPoolExecutor(max_workers=max_workers) as executor: + future_to_layer = { + executor.submit(is_valid_vector_layer, workspace, name): name + for name in layer_names + } + for future in as_completed(future_to_layer): + layer_name = future_to_layer[future] + try: + results[layer_name] = future.result() + except Exception as e: + logger.warning(f"Failed check for {layer_name}: {e}") + results[layer_name] = False + return results + + +@app.task(bind=True) +def missing_layer_for_all_workspace(self): + workspaces = ( + Dataset.objects.filter(workspace__isnull=False) + .values_list("workspace", flat=True) + .distinct() + ) + workspaces = [w.strip() for w in workspaces if w and w.strip()] + result = {} + for workspace in workspaces: + result[workspace] = check_missing_layers(workspace) + send_missing_layers_report(result) + return result + + +def check_missing_layers(workspace: str) -> dict: + logger.info(f"{workspace=}") + workspace_config = load_workspace_config() + workspace_types = get_workspace_types(workspace) + logger.info(f"Found types: {workspace_types}") + + if not workspace_types: + logger.critical(f"No config found for workspace: {workspace}") + return {"no config found": []} + + layer_config = get_layer_config_by_type( + workspace_config, workspace, workspace_types + ) + + # ── Fetch all available layers ONCE (bulk, cached) ────────────────────── + available_raster_layers = valid_raster_layers_for_workspace(workspace) + available_vector_layers = valid_vector_layers_for_workspace(workspace) # bulk WFS` + use_bulk_vector = bool(available_vector_layers) # fallback if WFS unavailable + + # ── Pre-build all (tehsil, layer_type, layer_name, state) combos ──────── + active_tehsils = TehsilSOI.objects.select_related( + "district__state" # eliminates N+1 DB queries + ).filter(active_status=True) + + tasks = [] # (state, layer_type, layer_name) + for tehsil_obj in active_tehsils: + state = tehsil_obj.district.state.state_name + district_name = valid_gee_text(tehsil_obj.district.district_name.lower()) + tehsil_name = valid_gee_text(tehsil_obj.tehsil_name.lower()) + + for layer_type, configs in layer_config.items(): + for config in configs: + prefix = config.get("prefix") + suffix = config.get("suffix") + layer_name = "_".join( + p for p in [prefix, district_name, tehsil_name, suffix] if p + ) + tasks.append( + (state, district_name, tehsil_name, layer_type, layer_name) + ) + + # ── Check raster layers (pure set lookup, no HTTP) ─────────────────────── + missing_layers = [] + vector_tasks = [] # collect for batch/parallel processing + + for state, district_name, tehsil_name, layer_type, layer_name in tasks: + if layer_type == "raster": + if layer_name not in available_raster_layers: + missing_layers.append(f"{state}, {district_name}, {tehsil_name}") + + elif layer_type == "vector": + if use_bulk_vector: + # O(1) set lookup — no HTTP call needed + if layer_name not in available_vector_layers: + missing_layers.append(f"{state}, {district_name}, {tehsil_name}") + else: + vector_tasks.append( + (state, district_name, tehsil_name, layer_name) + ) # queue for parallel check + + # ── Parallel fallback for vector layers (if WFS bulk fetch failed) ─────── + if vector_tasks: + layer_names = [ln for _, _, _, ln in vector_tasks] + info_by_name = {ln: (st, dn, tn) for st, dn, tn, ln in vector_tasks} + validity = bulk_check_vector_layers(workspace, layer_names) + + for layer_name, is_valid in validity.items(): + if not is_valid: + state, district_name, tehsil_name = info_by_name[layer_name] + missing_layers.append(f"{state}, {district_name}, {tehsil_name}") + + return {"missing_layers": missing_layers} + + +def get_workspace_types(workspace_name): + """ + Return all layer types for a given workspace name from DB. + Example: ['raster', 'vector'] + """ + return list( + Dataset.objects.filter(workspace=workspace_name) + .values_list("layer_type", flat=True) + .distinct() + ) + + +def get_layer_config_by_type(workspace_config, workspace_name, layer_types): + """ + Return list of prefix/suffix configs for each layer type. + + Args: + workspace_config (dict): config JSON + workspace_name (str): dataset name + layer_types (list): ['raster', 'vector'] + + Returns: + dict: {'raster': [{'prefix': ..., 'suffix': ...}, ...], 'vector': [...]} + """ + result = {} + + for config in workspace_config.values(): + if config.get("name") == workspace_name and config.get("type") in layer_types: + layer_type = config["type"] + + if layer_type not in result: + result[layer_type] = [] + + result[layer_type].append( + { + "prefix": config.get("prefix"), + "suffix": config.get("suffix"), + } + ) + + return result + + +def get_session_with_retry(): + """Creates a requests session with retry and timeout handling.""" + session = requests.Session() + retry = Retry( + total=3, + backoff_factor=1, # waits 1s, 2s, 4s between retries + status_forcelist=[500, 502, 503, 504], + ) + adapter = HTTPAdapter(max_retries=retry) + session.mount("http://", adapter) + session.mount("https://", adapter) + return session + + +def clear_layer_cache(workspace: str = None): + if workspace: + _raster_cache.pop(workspace, None) + _vector_cache.pop(workspace, None) + logger.info(f"Cleared cache for {workspace}") + else: + _raster_cache.clear() + _vector_cache.clear() + logger.info("Cleared all layer caches") + + +def refresh_layer_cache(request, workspace=None): + clear_layer_cache(workspace) + return Response({"message": f"Cache cleared for: {workspace or 'all workspaces'}"}) diff --git a/dpr/api.py b/dpr/api.py index 87911e34..37526005 100644 --- a/dpr/api.py +++ b/dpr/api.py @@ -4,6 +4,7 @@ from django.http import HttpResponse, HttpResponseBadRequest from django.shortcuts import render +from django.templatetags.i18n import language from django.urls import reverse from drf_yasg import openapi from drf_yasg.utils import swagger_auto_schema @@ -138,6 +139,7 @@ def generate_dpr(request): try: plan_id = request.data.get("plan_id") email_id = request.data.get("email_id") + # language = request.data.get("language") regenerate = request.data.get("regenerate", False) logger.info( @@ -572,13 +574,15 @@ def generate_village_report(request): for key, value in params.items(): result[key] = value - + context = { "state": result["state"], "district": result["district"], "block": result["block"], - "village_id" : result["villageId"], - "development_scores": json.dumps([0.85, 0.72, 0.65, 0.78, 0.82, 0.75, 0.68, 0.80, 0.75, 0.70]) # Serialize to JSON string + "village_id": result["villageId"], + "development_scores": json.dumps( + [0.85, 0.72, 0.65, 0.78, 0.82, 0.75, 0.68, 0.80, 0.75, 0.70] + ), # Serialize to JSON string } return render(request, "village-report.html", context) @@ -587,6 +591,7 @@ def generate_village_report(request): logger.exception("Exception in generate_village_report api :: ", e) return render(request, "error-page.html", {}) + # --------------------------------------------------------------------------- # DPR Data API # --------------------------------------------------------------------------- diff --git a/dpr/gen_dpr.py b/dpr/gen_dpr.py index 917ba55d..9aebfce0 100644 --- a/dpr/gen_dpr.py +++ b/dpr/gen_dpr.py @@ -190,9 +190,9 @@ def initialize_document(): doc = Document() heading = doc.add_heading("Detailed Project Report", 0) heading.alignment = WD_PARAGRAPH_ALIGNMENT.CENTER - doc.add_paragraph( - date.today().strftime("%B %d, %Y") - ).alignment = WD_PARAGRAPH_ALIGNMENT.CENTER + doc.add_paragraph(date.today().strftime("%B %d, %Y")).alignment = ( + WD_PARAGRAPH_ALIGNMENT.CENTER + ) return doc diff --git a/dpr/get_dpr_sectionwise_data.py b/dpr/get_dpr_sectionwise_data.py new file mode 100644 index 00000000..d47b2012 --- /dev/null +++ b/dpr/get_dpr_sectionwise_data.py @@ -0,0 +1,1291 @@ +import geopandas as gpd +from .gen_dpr import ( + get_settlement_coordinates_for_plan, + get_mws_uid_for_settlement_gdf, + get_data_for_settlement, + get_crops_data, + get_livestock_data, + get_all_wells_with_mws, + get_all_waterbodies_with_mws, + sort_key, +) +from .utils import to_utf8 +from collections import defaultdict +from dpr.utils import ensure_str, get_waterbody_repair_activities +from .mapping import populate_maintenance_from_waterbody +from .services import ( + get_nrm_works_data, +) +from .service.form_download_service import ( + load_form_labels, + translate_choice, + translate_multiple_choices, +) +from dpr.models import ( + GW_maintenance, + ODK_livelihood, + ODK_agrohorticulture, + Agri_maintenance, + SWB_maintenance, + SWB_RS_maintenance, + ODK_agri, + ODK_groundwater, +) + + +def get_section_b_data(plan, total_settlements, mws_fortnight): + + mws_gdf = gpd.GeoDataFrame.from_features(mws_fortnight["features"]) + + settlement_mws_ids = [] + settlement_coordinates = get_settlement_coordinates_for_plan(plan.id) + + for settlement_name, latitude, longitude in settlement_coordinates: + mws_uid = get_mws_uid_for_settlement_gdf(mws_gdf, latitude, longitude) + + if mws_uid: + settlement_mws_ids.append( + { + "settlement": settlement_name, + "mws_id": mws_uid, + } + ) + + centroid = None + + if settlement_mws_ids: + intersecting_mws = mws_gdf[ + mws_gdf["uid"].isin([item["mws_id"] for item in settlement_mws_ids]) + ] + + if not intersecting_mws.empty: + centroid = intersecting_mws.geometry.unary_union.centroid + + return ( + { + "village_name": to_utf8(plan.village_name), + "gram_panchayat": to_utf8(plan.gram_panchayat), + "tehsil": to_utf8(plan.tehsil_soi.tehsil_name), + "district": to_utf8(plan.district_soi.district_name), + "state": to_utf8(plan.state_soi.state_name), + "total_settlements": total_settlements, + "settlement_mws_pairs": settlement_mws_ids, + "village_coordinates": ( + f"{centroid.y:.8f}, {centroid.x:.8f}" if centroid else "Not available" + ), + }, + settlement_mws_ids, + mws_gdf, + ) + + +def get_section_c_data(plan, language): + settlement_data = get_data_for_settlement(plan.id) + labels = load_form_labels("Add_Settlements_form _V1.0.1") + crop_data = get_crops_data(plan.id) + crop_labels = load_form_labels("crop_form_V1.0.0") + for settlement in settlement_data: + + settlement.largest_caste_label = translate_choice( + labels, + "select_one_type", + settlement.largest_caste, + language, + ) + + if settlement.largest_caste.lower() == "single caste group": + + settlement.smallest_caste_label = translate_choice( + labels, + "caste_group_single", + settlement.smallest_caste, + language, + ) + + elif settlement.largest_caste.lower() == "mixed caste group": + + settlement.settlement_status_label = translate_multiple_choices( + labels, + "caste_group_single", + settlement.settlement_status, + language, + ) + settlement.nrega_past_work_label = translate_multiple_choices( + labels, + "work_demands", + clean_odk_value(settlement.nrega_past_work), + language, + ) + + settlement.nrega_demand_label = translate_choice( + labels, + "select_one_demands", + clean_odk_value(settlement.nrega_demand), + language, + ) + + settlement.nrega_issues_label = translate_multiple_choices( + labels, + "select_multiple_issues", + settlement.nrega_issues, + language, + ) + for crop in crop_data: + crop["irrigation_source"] = translate_multiple_choices( + crop_labels, + "select_multiple_widgets", + clean_odk_value(crop["irrigation_source"]), + language, + ) + + crop["land_classification"] = translate_choice( + crop_labels, + "select_one_classified", + clean_odk_value(crop["land_classification"]), + language, + ) + + crop["kharif_crops"] = translate_multiple_choices( + crop_labels, + "select_multiple_cropping_kharif", + clean_odk_value(crop["kharif_crops"]), + language, + ) + + crop["rabi_crops"] = translate_multiple_choices( + crop_labels, + "select_multiple_cropping_Rabi", + clean_odk_value(crop["rabi_crops"]), + language, + ) + + crop["zaid_crops"] = translate_multiple_choices( + crop_labels, + "select_multiple_cropping_Zaid", + clean_odk_value(crop["zaid_crops"]), + language, + ) + + crop["cropping_intensity"] = translate_choice( + crop_labels, + "select_one_productivity", + clean_odk_value(crop["cropping_intensity"]), + language, + ) + return { + "socio_eco": settlement_data, + "mgnrega": settlement_data, + "crop_info": crop_data, + "livestock_info": get_livestock_data(plan.id), + } + + +def get_section_d_data(plan, settlement_mws_ids, mws_gdf, language): + unique_mws_ids = sorted({item["mws_id"] for item in settlement_mws_ids}) + + all_wells_with_mws = get_all_wells_with_mws( + plan, + unique_mws_ids, + mws_gdf, + ) + + all_waterbodies_with_mws = get_all_waterbodies_with_mws( + plan, + unique_mws_ids, + mws_gdf, + ) + + well_labels = load_form_labels("Add_well_form_V1.0.1") + water_body_labels_repair = load_form_labels( + "NRM_form_NRM_form_Waterbody_Screen_V1.0.0" + ) + water_body_labels = load_form_labels("Add_Waterbodies_Form_V1.0.3") + return { + "mws": get_mws_table_data(unique_mws_ids, mws_gdf), + "well_summary": get_well_summary_data(all_wells_with_mws), + "wells": get_detailed_well_data(all_wells_with_mws, well_labels, language), + "water_summary": get_waterbody_summary_data( + all_waterbodies_with_mws, water_body_labels, language + ), + "water_structures": get_detailed_waterbody_data( + all_waterbodies_with_mws, + water_body_labels, + language, + water_body_labels_repair, + ), + } + + +def get_mws_table_data(unique_mws_ids, mws_gdf): + + data = [] + + for mws_id in unique_mws_ids: + + matching_feature = mws_gdf[mws_gdf["uid"] == mws_id] + + centroid = None + + if not matching_feature.empty: + c = matching_feature.geometry.centroid.iloc[0] + centroid = f"{c.y:.8f}, {c.x:.8f}" + + data.append( + { + "mws_id": mws_id, + "centroid": centroid, + } + ) + + return data + + +def get_well_summary_data(all_wells_with_mws): + + wells_count = defaultdict(int) + households_count = defaultdict(int) + + for well, _ in all_wells_with_mws: + + wells_count[well.beneficiary_settlement] += 1 + + households_count[well.beneficiary_settlement] += int( + well.households_benefitted or 0 + ) + + rows = [] + + for settlement in sorted(wells_count.keys(), key=sort_key): + + rows.append( + { + "settlement": settlement, + "num_wells": wells_count[settlement], + "households": households_count[settlement], + } + ) + + return rows + + +def get_detailed_well_data(all_wells_with_mws, labels, language): + + rows = [] + + all_wells_with_mws_sorted = sorted( + all_wells_with_mws, + key=lambda x: ( + not x[0].beneficiary_settlement or x[0].beneficiary_settlement == "NA", + (x[0].beneficiary_settlement or "").lower(), + ), + ) + + for well, mws_id in all_wells_with_mws_sorted: + + well_usage = None + + if well.data_well and "Well_usage" in well.data_well: + + usage = well.data_well["Well_usage"] + + used = ensure_str(usage.get("select_one_well_used")) + + other = usage.get("select_one_well_used_other") + + if used and used.lower() == "other" and other: + well_usage = f"Other: {other}" + + elif used: + well_usage = translate_choice( + labels, + "select_one_well_used", + used, + language, + ) + + repair_activities = None + + if well.data_well and "Well_usage" in well.data_well: + + usage = well.data_well["Well_usage"] + + repairs = ensure_str(usage.get("repairs_type")) + repairs_other = usage.get("repairs_type_other") + + if repairs and repairs.lower() == "other" and repairs_other: + repair_activities = repairs_other + + elif repairs: + repair_activities = translate_multiple_choices( + labels, + "repairs_type", + repairs, + language, + ) + rows.append( + { + "mws_id": mws_id, + "settlement": well.beneficiary_settlement, + "well_type": translate_choice( + labels, + "select_one_well_type", + well.data_well.get("select_one_well_type"), + language, + ), + "owner": translate_choice( + labels, + "select_one_owns", + well.owner, + language, + ), + "beneficiary_name": well.data_well.get("Beneficiary_name") or None, + "father_name": well.data_well.get("ben_father") or None, + "water_availability": translate_choice( + labels, + "select_one_year", + well.data_well.get("select_one_year"), + language, + ), + "households_benefitted": well.households_benefitted, + "caste_uses": translate_multiple_choices( + labels, + "select_multiple_caste_use", + well.caste_uses, + language, + ), + "well_usage": well_usage, + "need_maintenance": translate_choice( + labels, + "is_maintenance_required", + well.need_maintenance, + language, + ), + "repair_activities": repair_activities, + "latitude": well.latitude, + "longitude": well.longitude, + } + ) + + return rows + + +def get_waterbody_summary_data(all_waterbodies_with_mws, water_body_labels, language): + + waterbody_count = defaultdict(int) + + households_count = defaultdict(int) + + for waterbody, _ in all_waterbodies_with_mws: + + structure_type = waterbody.water_structure_type + + key = ( + waterbody.beneficiary_settlement, + structure_type, + ) + + waterbody_count[key] += 1 + + households_count[key] += int(waterbody.household_benefitted or 0) + + rows = [] + + for ( + settlement, + structure_type, + ) in sorted( + waterbody_count.keys(), + key=lambda x: sort_key(x[0]), + ): + + rows.append( + { + "settlement": settlement, + "structure_type": translate_choice( + water_body_labels, + "select_one_water_structure", + structure_type, + language, + ), + "count": waterbody_count[(settlement, structure_type)], + "households": households_count[(settlement, structure_type)], + } + ) + + return rows + + +def get_detailed_waterbody_data( + all_waterbodies_with_mws, waterbody_labels, language, water_body_labels_repair +): + + rows = [] + + for ( + waterbody, + mws_id, + ) in sorted( + all_waterbodies_with_mws, + key=lambda x: sort_key(x[0].beneficiary_settlement), + ): + + who_manages = waterbody.who_manages or None + + if who_manages: + + if who_manages.lower() == "other": + who_manages = "Other: " + (waterbody.specify_other_manager or "") + + else: + who_manages = translate_choice( + waterbody_labels, + "select_one_manages", + who_manages.lower(), + language, + ) + + structure_type = waterbody.water_structure_type or None + structure_type_eng = structure_type + + if structure_type: + + if structure_type.lower() == "other": + structure_type = "Other: " + (waterbody.water_structure_other or "") + structure_type_eng = structure_type + + else: + structure_type = translate_choice( + waterbody_labels, + "select_one_water_structure", + structure_type, + language, + ) + repair_activities = get_waterbody_repair_activities( + waterbody.data_waterbody, + structure_type_eng, + ) + repair_label_key = REPAIR_LABEL_MAPPING.get( + str(waterbody.water_structure_type).strip().lower() + ) + wb_owner = waterbody.owner + wb_owner = classify_demand_type(wb_owner.lower()) + rows.append( + { + "mws_id": mws_id, + "settlement": waterbody.beneficiary_settlement, + "owner": translate_choice( + water_body_labels_repair, + "demand_type", + wb_owner, + language, + ), + "beneficiary_name": waterbody.data_waterbody.get("Beneficiary_name") + or None, + "father_name": waterbody.data_waterbody.get("ben_father") or None, + "who_manages": who_manages, + "caste_uses": translate_multiple_choices( + waterbody_labels, + "select_multiple_caste_use", + waterbody.caste_who_uses, + language, + ), + "households_benefitted": waterbody.household_benefitted, + "structure_type": structure_type, + "usage": translate_multiple_choices( + waterbody_labels, + "select_multiple_uses_structure", + waterbody.data_waterbody.get("select_multiple_uses_structure"), + language, + ), + "need_maintenance": translate_choice( + waterbody_labels, + "select_one_maintenance", + waterbody.need_maintenance, + language, + ), + "repair_activities": translate_multiple_choices( + water_body_labels_repair, + repair_label_key, + repair_activities, + language, + ), + "latitude": waterbody.latitude, + "longitude": waterbody.longitude, + } + ) + + return rows + + +def get_section_e_data(plan, language): + populate_maintenance_from_waterbody(plan) + gw_data = get_maintenance_data(plan.id, "gw") + agri_data = get_maintenance_data(plan.id, "agri") + swb_data = get_maintenance_data(plan.id, "swb") + swb_rs_data = get_maintenance_data(plan.id, "swb_rs") + gw_label = load_form_labels( + "Propose_Maintenance_on_Existing_Water_Recharge_Structures_V1.1.1" + ) + agri_label = load_form_labels( + "Propose_Maintenance_on_Existing_Irrigation_Structures_V1.1.1" + ) + swb_rs_label = load_form_labels("PM_Remote_Sensed_Surface_Water_structure_V1.0.0") + swb_label = load_form_labels("NRM_form_NRM_form_Waterbody_Screen_V1.0.0") + + for row in gw_data: + + row["demand_type"] = translate_choice( + gw_label, + "demand_type", + row["demand_type"], + language, + ) + + row["structure_type"] = translate_choice( + gw_label, + "select_one_recharge_structure", + row["structure_type"], + language, + ) + for row in agri_data: + demand_type = classify_demand_type(row["demand_type"].lower()) + row["demand_type"] = translate_choice( + agri_label, + "demand_type".lower().replace(" ", "_"), + demand_type, + language, + ) + row["structure_type"] = translate_choice( + agri_label, + "select_one_irrigation_structure", + row["structure_type"], + language, + ) + for row in swb_data: + row["demand_type"] = translate_choice( + swb_label, + "demand_type", + row["demand_type"], + language, + ) + row["structure_type"] = translate_choice( + swb_rs_label, + "TYPE_OF_WORK", + row["structure_type"], + language, + ) + for row in swb_rs_data: + row["demand_type"] = translate_choice( + swb_rs_label, + "demand_type", + row["demand_type"], + language, + ) + + original_structure = row["structure_type"] + original_structure = str(original_structure).strip().lower() + repair_key = RS_WATER_STRUCTIRE_REVERSE_MAPPING.get(original_structure) + if repair_key and row.get("repair_activities"): + row["repair_activities"] = translate_choice( + swb_rs_label, + repair_key, + clean_odk_value(row["repair_activities"]), + language, + ) + row["structure_type"] = translate_choice( + swb_rs_label, + "TYPE_OF_WORK", + row["structure_type"], + language, + ) + return { + "gw": gw_data, + "agri": agri_data, + "swb": swb_data, + "swb_rs": swb_rs_data, + } + + +def get_section_f_data(plan, language): + gw_labels = load_form_labels("NRM_form_propose_new_recharge_structure_V1.0.0") + agri_labels = load_form_labels("NRM_form_Agri_Screen_V1.0.0") + works = get_nrm_works_data(plan.id) + for row in works: + if row["work_category"] == "Recharge Structure": + row["demand_type"] = translate_choice( + gw_labels, + "demand_type", + row["demand_type"], + language, + ) + row["work_demand"] = translate_choice( + gw_labels, + "TYPE_OF_WORK_ID", + row["work_demand"], + language, + ) + row["gender"] = translate_choice( + gw_labels, + "select_gender", + row["gender"], + language, + ) + row["work_category"] = translate_work_category( + row["work_category"], + language, + ) + elif row["work_category"] == "Irrigation Work": + row["demand_type"] = translate_choice( + agri_labels, + "demand_type_irrigation", + row["demand_type"], + language, + ) + row["work_demand"] = translate_choice( + agri_labels, + "TYPE_OF_WORK_ID", + row["work_demand"], + language, + ) + row["gender"] = translate_choice( + agri_labels, + "gender", + row["gender"], + language, + ) + row["work_category"] = translate_work_category( + row["work_category"], + language, + ) + return {"works": works} + + +def get_section_g_data(plan, language): + all_livelihood = get_livelihood_data(plan.id) + agro_labels = load_form_labels("Agrohorticulture") + livelihood_labels = load_form_labels("NRM Livelihood Form") + + livestock_fisheries = [ + r for r in all_livelihood if r["livelihood_work"] in ("Livestock", "Fisheries") + ] + for row in livestock_fisheries: + livelihood_work = row.get("livelihood_work") + + if livelihood_work == "Livestock": + row["demand_type"] = translate_choice( + livelihood_labels, + "livestock_demand", + row.get("demand_type"), + language, + ) + + row["work_demand"] = translate_choice( + livelihood_labels, + "demands_promoting_livestock", + row.get("work_demand"), + language, + ) + row["gender"] = translate_choice( + livelihood_labels, + "gender_livestock", + row.get("gender"), + language, + ) + + elif livelihood_work == "Fisheries": + row["demand_type"] = translate_choice( + livelihood_labels, + "demand_type_fisheries", + row.get("demand_type"), + language, + ) + + row["work_demand"] = translate_choice( + livelihood_labels, + "select_one_promoting_fisheries", + row.get("work_demand"), + language, + ) + row["gender"] = translate_choice( + livelihood_labels, + "gender_fisheries", + row.get("gender"), + language, + ) + + plantations_etc = [ + r + for r in all_livelihood + if r["livelihood_work"] not in ("Livestock", "Fisheries") + ] + for row in plantations_etc: + livelihood_work = row.get("livelihood_work") + + if livelihood_work == "Plantations": + + row["demand_type"] = translate_choice( + agro_labels, + "demand_type_plantations", + row.get("demand_type"), + language, + ) + row["gender"] = translate_choice( + agro_labels, + "gender", + row.get("gender"), + language, + ) + + species = row.get("work_demand") + + if species: + translated_species = [] + + for item in species.split(): + translated_species.append( + translate_choice( + agro_labels, + "select_multiple_species", + item.strip().lower(), + language, + ) + ) + + row["work_demand"] = ", ".join(translated_species) + + elif livelihood_work == "Kitchen Garden": + + row["demand_type"] = translate_choice( + livelihood_labels, + "demand_type_kitchen_garden", + row.get("demand_type"), + language, + ) + row["gender"] = translate_choice( + livelihood_labels, + "gender_kitchen_gardens", + row.get("gender"), + language, + ) + for row in livestock_fisheries: + row["livelihood_work"] = translate_livelihood_work( + row["livelihood_work"], + language, + ) + + for row in plantations_etc: + row["livelihood_work"] = translate_livelihood_work( + row["livelihood_work"], + language, + ) + return { + "livestock_fisheries": livestock_fisheries, + "plantations": plantations_etc, + } + + +def clean_odk_value(value): + if value is None: + return None + + value = str(value).strip() + + if value.upper() == "NA": + return None + + return value + + +REPAIR_LABEL_MAPPING = { + "community pond": "select_one_community_pond", + "large water body": "select_one_repair_large_water_body", + "farm pond": "select_one_farm_pond", + "canal": "select_one_repair_canal", + "check dam": "select_one_check_dam", + "percolation tank": "select_one_percolation_tank", + "rock fill dam": "select_one_rock_fill_dam", + "loose boulder structure": "select_one_loose_boulder_structure", + "5% model structure": "select_one_model5_structure", + "30-40 model structure": "select_one_model30_40_structure", +} +REPAIR_ACTIVITY_MAPPING = { + "check dam": "select_one_check_dam", + "percolation tank": "select_one_percolation_tank", + "earthen gully plug": "select_one_earthen_gully_plug", + "drainage/soakage channels": "select_one_drainage_soakage_channels", + "recharge pits": "select_one_recharge_pits", + "sokage pits": "select_one_sokage_pits", + "trench cum bund network": "select_one_trench_cum_bund_network", + "continuous contour trenches (cct)": "select_one_continuous_contour_trenches", + "staggered contour trenches(sct)": "select_one_staggered_contour_trenches", + "water absorption trenches(wat)": "select_one_water_absorption_trenches", + "loose boulder structure": "select_one_loose_boulder_structure", + "rock fill dam": "select_one_rock_fill_dam", + "stone bunding": "select_one_stone_bunding", + "diversion drains": "select_one_diversion_drains", + "bunding:contour bunds/ graded bunds": "select_one_bunding", + "5% model structure": "select_one_model5_structure", + "30-40 model structure": "select_one_model30_40_structure", +} + + +def get_maintenance_data(plan_id, maintenance_type): + pid = str(plan_id) + result = [] + + if maintenance_type == "gw": + for m in GW_maintenance.objects.filter(plan_id=pid).exclude(is_deleted=True): + d = m.data_gw_maintenance or {} + structure_type = d.get("select_one_recharge_structure") or None + repair = _resolve_repair_activity( + d, + structure_type, + RECHARGE_STRUCTURE_MAPPING, + ) + result.append( + { + "id": m.gw_maintenance_id, + "demand_type": d.get("demand_type"), + "beneficiary_settlement": d.get("beneficiary_settlement"), + "beneficiary_name": d.get("Beneficiary_Name"), + "gender": d.get("select_gender"), + "beneficiary_father_name": d.get("ben_father"), + "structure_type": structure_type, + "repair_activities": repair, + "latitude": m.latitude, + "longitude": m.longitude, + } + ) + + elif maintenance_type == "agri": + for m in Agri_maintenance.objects.filter(plan_id=pid).exclude(is_deleted=True): + d = m.data_agri_maintenance or {} + structure_type = ( + d.get("select_one_water_structure") + or d.get("select_one_irrigation_structure") + or "NA" + ) + repair = _resolve_repair_activity( + d, structure_type, IRRIGATION_STRUCTURE_REVERSE_MAPPING + ) + result.append( + { + "id": m.agri_maintenance_id, + "demand_type": d.get("demand_type"), + "beneficiary_settlement": d.get("beneficiary_settlement"), + "beneficiary_name": d.get("Beneficiary_Name"), + "beneficiary_father_name": d.get("ben_father"), + "structure_type": structure_type, + "repair_activities": repair, + "latitude": m.latitude, + "longitude": m.longitude, + } + ) + + elif maintenance_type == "swb": + for m in SWB_maintenance.objects.filter(plan_id=pid).exclude(is_deleted=True): + d = m.data_swb_maintenance or {} + structure_type = ( + d.get("TYPE_OF_WORK") or d.get("select_one_water_structure") or "NA" + ) + repair = _resolve_repair_activity( + d, structure_type, WATER_STRUCTURE_REVERSE_MAPPING + ) + result.append( + { + "id": m.swb_maintenance_id, + "demand_type": d.get("demand_type"), + "beneficiary_settlement": d.get("beneficiary_settlement"), + "beneficiary_name": d.get("Beneficiary_Name"), + "gender": d.get("select_gender"), + "beneficiary_father_name": d.get("ben_father"), + "structure_type": structure_type, + "repair_activities": repair, + "latitude": m.latitude, + "longitude": m.longitude, + } + ) + + elif maintenance_type == "swb_rs": + for m in SWB_RS_maintenance.objects.filter(plan_id=pid).exclude( + is_deleted=True + ): + d = m.data_swb_rs_maintenance or {} + structure_type = d.get("TYPE_OF_WORK") or "NA" + repair = _resolve_repair_activity( + d, structure_type, RS_WATER_STRUCTIRE_REVERSE_MAPPING + ) + + result.append( + { + "id": m.swb_rs_maintenance_id, + "demand_type": d.get("demand_type"), + "beneficiary_settlement": d.get("beneficiary_settlement"), + "beneficiary_name": d.get("Beneficiary_Name"), + "gender": d.get("select_gender"), + "beneficiary_father_name": d.get("ben_father"), + "structure_type": structure_type, + "repair_activities": repair, + "latitude": m.latitude, + "longitude": m.longitude, + } + ) + + return result + + +def _resolve_repair_activity( + data, structure_type, mapping, fallback_key="select_one_activities" +): + repair_activities = None + + structure_key = str(structure_type).strip().lower() + + repair_key = mapping.get(structure_key) + + if repair_key: + repair_activities = data.get(repair_key) + + if repair_activities == "other": + repair_activities = data.get(f"{repair_key}_other") + + if not repair_activities: + repair_activities = data.get(fallback_key) + + return repair_activities + + +RECHARGE_STRUCTURE_MAPPING = { + "Check dam": "select_one_check_dam", + "Percolation tank": "select_one_percolation_tank", + "Earthen gully plug": "select_one_earthen_gully_plug", + "Drainage/soakage channels": "select_one_drainage_soakage_channels", + "Recharge pits": "select_one_recharge_pits", + "Sokage pits": "select_one_sokage_pits", + "Trench cum bund network": "select_one_trench_cum_bund_network", + "Continuous contour trenches (CCT)": "select_one_continuous_contour_trenches", + "Staggered Contour trenches(SCT)": "select_one_staggered_contour_trenches", + "Water absorption trenches(WAT)": "select_one_water_absorption_trenches", + "Loose boulder structure": "select_one_loose_boulder_structure", + "Rock fill dam": "select_one_rock_fill_dam", + "Stone bunding": "select_one_stone_bunding", + "Diversion drains": "select_one_diversion_drains", + "Bunding:Contour bunds/ graded bunds": "select_one_bunding", + "5% model structure": "select_one_model5_structure", + "30-40 model structure": "select_one_model30_40_structure", +} + + +def get_livelihood_data(plan_id): + pid = str(plan_id) + result = [] + + for record in ( + ODK_livelihood.objects.filter(plan_id=pid) + .exclude(status_re="rejected") + .exclude(is_deleted=True) + ): + dl = record.data_livelihood or {} + + livestock_group = dl.get("Livestock") or {} + fisheries_group = dl.get("fisheries") or {} + plantation_group = dl.get("plantations") or {} + kitchen_garden_group = dl.get("kitchen_gardens") or {} + + is_livestock = ( + ensure_str(livestock_group.get("is_demand_livestock", "")).lower() == "yes" + or ensure_str(dl.get("select_one_demand_promoting_livestock", "")).lower() + == "yes" + ) + if is_livestock: + demands = ensure_str(livestock_group.get("demands_promoting_livestock")) + if demands and demands.lower() == "other": + demands = livestock_group.get("demands_promoting_livestock_other") + if not demands: + demands = ensure_str(dl.get("select_one_promoting_livestock")) + if demands and demands.lower() == "other": + demands = dl.get("select_one_promoting_livestock_other") + result.append( + { + "livelihood_work": "Livestock", + "demand_type": livestock_group.get("livestock_demand"), + "work_demand": demands, + "beneficiary_settlement": record.beneficiary_settlement, + "beneficiary_name": dl.get("beneficiary_name") + or livestock_group.get("ben_livestock"), + "gender": livestock_group.get("gender_livestock"), + "beneficiary_father_name": livestock_group.get( + "ben_father_livestock" + ), + "latitude": record.latitude, + "longitude": record.longitude, + } + ) + + is_fisheries = ( + ensure_str(fisheries_group.get("is_demand_fisheris", "")).lower() == "yes" + or ensure_str(dl.get("select_one_demand_promoting_fisheries", "")).lower() + == "yes" + ) + if is_fisheries: + demands = ensure_str(fisheries_group.get("select_one_promoting_fisheries")) + if demands and demands.lower() == "other": + demands = fisheries_group.get("select_one_promoting_fisheries_other") + if not demands: + demands = ensure_str(dl.get("select_one_promoting_fisheries")) + if demands and demands.lower() == "other": + demands = dl.get("select_one_promoting_fisheries_other") + result.append( + { + "livelihood_work": "Fisheries", + "demand_type": fisheries_group.get("demand_type_fisheries"), + "work_demand": demands, + "beneficiary_settlement": record.beneficiary_settlement, + "beneficiary_name": dl.get("beneficiary_name") + or fisheries_group.get("ben_fisheries"), + "gender": fisheries_group.get("gender_fisheries"), + "beneficiary_father_name": fisheries_group.get( + "ben_father_fisheries" + ), + "latitude": record.latitude, + "longitude": record.longitude, + } + ) + + is_plantation = ( + ensure_str(dl.get("select_one_demand_plantation", "")).lower() == "yes" + or ensure_str(plantation_group.get("select_plantation_demands", "")).lower() + == "yes" + ) + if is_plantation: + result.append( + { + "livelihood_work": "Plantations", + "demand_type": plantation_group.get("demand_type_plantations"), + "work_demand": dl.get("Plantation") + or plantation_group.get("crop_name"), + "beneficiary_settlement": record.beneficiary_settlement, + "beneficiary_name": dl.get("beneficiary_name") + or plantation_group.get("ben_plantation"), + "gender": plantation_group.get("gender"), + "beneficiary_father_name": plantation_group.get("ben_father"), + "total_acres": dl.get("Plantation_crop") + or plantation_group.get("crop_area"), + "latitude": record.latitude, + "longitude": record.longitude, + } + ) + + is_kitchen_garden = ( + ensure_str(dl.get("indi_assets", "")).lower() == "yes" + or ensure_str(kitchen_garden_group.get("assets_kg", "")).lower() == "yes" + ) + if is_kitchen_garden: + result.append( + { + "livelihood_work": "Kitchen Garden", + "demand_type": kitchen_garden_group.get( + "demand_type_kitchen_garden" + ), + "work_demand": dl.get("Plantation"), + "beneficiary_settlement": record.beneficiary_settlement, + "beneficiary_name": dl.get("beneficiary_name") + or kitchen_garden_group.get("ben_kitchen_gardens"), + "gender": kitchen_garden_group.get("gender_kitchen_gardens"), + "beneficiary_father_name": kitchen_garden_group.get( + "ben_father_kitchen_gardens" + ), + "total_acres": dl.get("area_didi_badi") + or kitchen_garden_group.get("area_kg"), + "latitude": record.latitude, + "longitude": record.longitude, + } + ) + + for agrohorti in ( + ODK_agrohorticulture.objects.filter(plan_id=pid) + .exclude(status_re="rejected") + .exclude(is_deleted=True) + ): + data = agrohorti.data_agohorticulture or {} + species_parts = filter( + None, + [ + data.get("select_multiple_species"), + data.get("select_multiple_species_other"), + ], + ) + species = " ".join(species_parts) or None + result.append( + { + "livelihood_work": "Plantations", + "demand_type": data.get("demand_type_plantations"), + "work_demand": species, + "beneficiary_settlement": data.get("beneficiary_settlement"), + "beneficiary_name": data.get("beneficiary_name"), + "gender": data.get("gender"), + "beneficiary_father_name": data.get("ben_father"), + "total_acres": data.get("crop_area"), + "latitude": agrohorti.latitude, + "longitude": agrohorti.longitude, + } + ) + + return result + + +def translate_livelihood_work(value, language): + translations = { + "hi": { + "Livestock": "पशुपालन", + "Fisheries": "मत्स्य पालन", + "Plantations": "पौधारोपण", + "Kitchen Garden": "रसोई बाड़ी", + }, + "od": { + "Livestock": "ପଶୁପାଳନ", + "Fisheries": "ମତ୍ସ୍ୟଚାଷ", + "Plantations": "ବୃକ୍ଷରୋପଣ", + "Kitchen Garden": "ରୋଷେଇ ବଗିଚା", + }, + } + + return translations.get(language, {}).get(value, value) + + +def get_nrm_works_data( + plan_id, +): + pid = str(plan_id) + result = [] + + for structure in ( + ODK_groundwater.objects.filter(plan_id=pid) + .exclude(status_re="rejected") + .exclude(is_deleted=True) + ): + dg = structure.data_groundwater or {} + result.append( + { + "work_category": "Recharge Structure", + "demand_type": dg.get("demand_type"), + "work_demand": structure.work_type, + "beneficiary_settlement": structure.beneficiary_settlement, + "beneficiary_name": dg.get("Beneficiary_Name"), + "gender": dg.get("select_gender"), + "beneficiary_father_name": dg.get("ben_father"), + "latitude": structure.latitude, + "longitude": structure.longitude, + } + ) + + for irr in ( + ODK_agri.objects.filter(plan_id=pid) + .exclude(status_re="rejected") + .exclude(is_deleted=True) + ): + da = irr.data_agri or {} + work_demand = irr.work_type + if (irr.work_type or "").lower() == "other": + work_demand = da.get("TYPE_OF_WORK_ID_other") or "Other (unspecified)" + result.append( + { + "work_category": "Irrigation Work", + "demand_type": da.get("demand_type_irrigation"), + "work_demand": work_demand, + "beneficiary_settlement": irr.beneficiary_settlement, + "beneficiary_name": da.get("Beneficiary_Name"), + "gender": da.get("gender"), + "beneficiary_father_name": da.get("ben_father"), + "latitude": irr.latitude, + "longitude": irr.longitude, + } + ) + + return result + + +def translate_work_category(value, language): + + translations = { + "hi": { + "Recharge Structure": "पुनर्भरण संरचना", + "Irrigation Work": "सिंचाई कार्य", + }, + "od": { + "Recharge Structure": "ପୁନର୍ଭରଣ ସଂରଚନା", + "Irrigation Work": "ସିଚାଇ କାର୍ଯ୍ୟ", + }, + } + + return translations.get(language, {}).get(value, value) + + +_COMMUNITY_DEMAND_VALUES = { + "community", + "community well", + "community demand", + "public", + "public well", + "shared among families", +} +_INDIVIDUAL_DEMAND_VALUES = {"private", "privately owned", "individual demand"} + + +def classify_demand_type(raw_value): + if not raw_value: + return raw_value + normalized = raw_value.strip().lower().replace("_", " ") + if normalized in _COMMUNITY_DEMAND_VALUES: + return "community_demand" + if normalized in _INDIVIDUAL_DEMAND_VALUES: + return "individual_demand" + return raw_value + + +IRRIGATION_STRUCTURE_MAPPING = { + "select_one_farm_pond": "Farm pond", + "select_one_community_pond": "Community Pond", + "select_one_well": "Well", + "select_one_canal": "Canal", + "select_one_farm_bund": "Farm bund", +} + +IRRIGATION_STRUCTURE_REVERSE_MAPPING = { + v.lower(): k for k, v in IRRIGATION_STRUCTURE_MAPPING.items() +} + +RS_WATER_STRUCTURE_MAPPING = { + "select_one_farm_pond": "Farm pond", + "select_one_community_pond": "Community Pond", + "select_one_repair_large_water_body": "Large water body", + "select_one_repair_canal": "Canal", + "select_one_check_dam": "Check dam", + "select_one_percolation_tank": "Percolation tank", + "select_one_rock_fill_dam": "Rock fill dam", + "select_one_loose_boulder_structure": "Loose boulder structure", + "select_one_model5_structure": "5% Model structure", + "select_one_Model30_40_structure": "30-40 Model structure", +} + +RS_WATER_STRUCTIRE_REVERSE_MAPPING = { + v.lower(): k for k, v in RS_WATER_STRUCTURE_MAPPING.items() +} + + +WATER_STRUCTURE_MAPPING = { + "select_one_farm_pond": "Farm pond", + "select_one_community_pond": "Community Pond", + "select_one_repair_large_water_body": "Large water body", + "select_one_repair_canal": "Canal", + "select_one_check_dam": "Check dam", + "select_one_percolation_tank": "Percolation tank", + "select_one_rock_fill_dam": "Rock fill dam", + "select_one_loose_boulder_structure": "Loose boulder structure", + "select_one_model5_structure": "5% Model structure", + "select_one_Model30_40_structure": "30-40 Model structure", +} + +WATER_STRUCTURE_REVERSE_MAPPING = { + v.lower(): k for k, v in WATER_STRUCTURE_MAPPING.items() +} diff --git a/dpr/models.py b/dpr/models.py index 83f7b33e..2959b52d 100644 --- a/dpr/models.py +++ b/dpr/models.py @@ -4,6 +4,7 @@ from django.db.models import Max from django.db.models.functions import Greatest +from django.utils import timezone DPR_STATUS_CHOICES = [ ("PENDING", "PENDING"), @@ -664,8 +665,9 @@ def get_latest_change_time(plan_id): latest_moderation=Max("moderated_at"), ) for key in ("latest_submission", "latest_deletion", "latest_moderation"): - if agg[key]: - times.append(agg[key]) + dt = normalize_datetime(agg[key]) + if dt: + times.append(dt) return max(times) if times else None def needs_regeneration(self): @@ -675,3 +677,13 @@ def needs_regeneration(self): if not latest_change: return False return latest_change > self.dpr_generated_at + + +def normalize_datetime(dt): + if not dt: + return None + + if timezone.is_naive(dt): + return timezone.make_aware(dt, timezone.get_current_timezone()) + + return dt diff --git a/dpr/service/__init__.py b/dpr/service/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/dpr/service/form_download_service.py b/dpr/service/form_download_service.py new file mode 100644 index 00000000..4d2db4c7 --- /dev/null +++ b/dpr/service/form_download_service.py @@ -0,0 +1,249 @@ +import json +from pathlib import Path +import requests +from utilities.constants import ODK_BASE_URL, ODK_PROJECT_ID +from django.conf import settings +from plans.utils import fetch_bearer_token +from nrm_app.settings import ODK_USERNAME, ODK_PASSWORD +import pandas as pd +from utilities.logger import setup_logger +import re + +logger = setup_logger(__name__) + + +def sync_odk_forms(): + """ + Downloads ODK XLSX forms only when: + 1. Form version has changed, OR + 2. XLSX file is missing locally. + + Returns: + dict: Summary of downloaded and skipped forms. + """ + + token = fetch_bearer_token(ODK_USERNAME, ODK_PASSWORD) + headers = {"Authorization": f"Bearer {token}"} + + forms_dir = Path(settings.BASE_DIR) / "data" / "dpr" / "forms" + label_dirs = Path(settings.BASE_DIR) / "data" / "dpr" / "labels" + versions_file = Path(settings.BASE_DIR) / "data" / "odk" / "form_version.json" + + forms_dir.mkdir(parents=True, exist_ok=True) + versions_file.parent.mkdir(parents=True, exist_ok=True) + + if versions_file.exists(): + with open(versions_file, "r") as f: + local_versions = json.load(f) + else: + local_versions = {} + + response = requests.get( + f"{ODK_BASE_URL}{ODK_PROJECT_ID}/forms", + headers=headers, + ) + response.raise_for_status() + + forms = response.json() + + downloaded = [] + skipped = [] + + for form in forms: + form_id = form["xmlFormId"] + current_version = form.get("version") + + saved_version = local_versions.get(form_id) + file_path = forms_dir / f"{form_id}.xlsx" + label_path = label_dirs / f"{form_id}.json" + if ( + saved_version == current_version + and file_path.exists() + and label_path.exists() + ): + skipped.append(form_id) + continue + + print(f"Downloading {form_id} " f"(old={saved_version}, new={current_version})") + + file_response = requests.get( + f"{ODK_BASE_URL}{ODK_PROJECT_ID}/forms/{form_id}.xlsx", + headers=headers, + ) + file_response.raise_for_status() + + with open(file_path, "wb") as f: + f.write(file_response.content) + + local_versions[form_id] = current_version + downloaded.append(form_id) + try: + generate_labels_json( + file_path, + label_path, + ) + except Exception: + logger.exception(f"Failed to generate labels for {form_id}") + with open(versions_file, "w") as f: + json.dump(local_versions, f, indent=2) + + return { + "downloaded": downloaded, + "skipped": skipped, + "total_forms": len(forms), + } + + +def get_select_question_mapping(survey_df): + """ + Returns: + { + "gender": "gender", + "caste_group": "caste" + } + """ + + mapping = {} + + for _, row in survey_df.iterrows(): + + question_type = str(row.get("type", "")).strip() + question_name = row.get("name") + + if pd.isna(question_name): + continue + + question_name = str(question_name).strip() + + parts = question_type.split() + + if len(parts) < 2: + continue + + if parts[0] in ["select_one", "select_multiple"]: + mapping[question_name] = str(parts[1]).strip() + + return mapping + + +def generate_labels_json(form_path, output_path): + """ + Generate labels JSON from an XLSForm. + """ + + survey_df = pd.read_excel(form_path, sheet_name="survey") + choices_df = pd.read_excel(form_path, sheet_name="choices") + + # normalize list_name column + choices_df["list_name"] = choices_df["list_name"].astype(str).str.strip() + + question_mapping = get_select_question_mapping(survey_df) + + language_columns = [ + column for column in choices_df.columns if str(column).startswith("label::") + ] + + labels = {} + + for question_name, list_name in question_mapping.items(): + + question_name = str(question_name).strip() + list_name = str(list_name).strip() + + labels[question_name] = {} + + choice_rows = choices_df[choices_df["list_name"] == list_name] + + for _, row in choice_rows.iterrows(): + + choice_value = row.get("name") + + if pd.isna(choice_value): + continue + + choice_value = str(choice_value).strip().lower() + + labels[question_name][choice_value] = {} + + for column in language_columns: + + language = str(column).replace("label::", "").split("(")[0].strip() + + value = row.get(column) + + labels[question_name][choice_value][language] = ( + None if pd.isna(value) else str(value).strip() + ) + + output_path.parent.mkdir(parents=True, exist_ok=True) + + with open(output_path, "w", encoding="utf-8") as f: + json.dump(labels, f, ensure_ascii=False, indent=2) + + return labels + + +LANGUAGE_MAP = { + "en": "English", + "hi": "Hindi", + "gu": "Gujarati", + "kn": "Kannada", + "or": "Odia", +} + + +def load_form_labels(form_id): + label_file = Path(settings.BASE_DIR) / "data" / "dpr" / "labels" / f"{form_id}.json" + + if not label_file.exists(): + return {} + + with open(label_file, encoding="utf-8") as f: + return json.load(f) + + +def translate_choice(labels, field_name, value, language="en"): + if not value: + return value + + language_name = LANGUAGE_MAP.get(language, "English") + + normalized_labels = {str(key).strip(): val for key, val in labels.items()} + + field_labels = normalized_labels.get( + str(field_name).strip(), + {}, + ) + + value_normalized = str(value).strip().lower().replace("'", "’") + + translations = field_labels.get(value_normalized) + + if translations: + return translations.get(language_name, value) + + return value + + +def translate_multiple_choices( + labels, + field_name, + value, + language="en", +): + if not value: + return value + + values = [v.strip() for v in re.split(r"[,\s]+", str(value).strip()) if v.strip()] + + translated = [ + translate_choice( + labels, + field_name, + item, + language, + ) + for item in values + ] + + return ", ".join(translated) diff --git a/dpr/service/translation_service.py b/dpr/service/translation_service.py new file mode 100644 index 00000000..cb76f32a --- /dev/null +++ b/dpr/service/translation_service.py @@ -0,0 +1,16 @@ +import json +from pathlib import Path +from django.conf import settings + +TRANSLATION_DIR = Path(settings.BASE_DIR) / "data" / "dpr" / "translations" + + +def load_translations(language="en"): + + file_path = TRANSLATION_DIR / f"{language}.json" + + if not file_path.exists(): + file_path = TRANSLATION_DIR / "en.json" + + with open(file_path, "r", encoding="utf-8") as file: + return json.load(file) diff --git a/dpr/tasks.py b/dpr/tasks.py index b9082826..a22ac1d0 100644 --- a/dpr/tasks.py +++ b/dpr/tasks.py @@ -1,114 +1,123 @@ -from django.utils import timezone -from nrm_app.celery import app -from utilities.logger import setup_logger -import requests - -from .gen_dpr import ( - create_dpr_document, - get_mws_ids_for_report, - get_plan_details, -) -from .models import DPR_Report -from .utils import ( - transform_name, - send_dpr_email, - upload_dpr_to_s3, - check_dpr_exists_on_s3, -) - -logger = setup_logger(__name__) - - -def get_or_generate_dpr(plan, regenerate=False): - dpr_report, created = DPR_Report.objects.get_or_create( - plan_id=plan, - defaults={"plan_name": plan.plan, "status": "PENDING"} - ) - - if not regenerate: - s3_exists = check_dpr_exists_on_s3(dpr_report.dpr_report_s3_url) - if not created and not dpr_report.needs_regeneration() and s3_exists: - logger.info(f"Using cached DPR for plan {plan.id} from S3: {dpr_report.dpr_report_s3_url}") - return dpr_report, False - - logger.info(f"Generating new DPR for plan {plan.id}") - dpr_report.status = "GENERATING" - dpr_report.save(update_fields=["status"]) - - doc = create_dpr_document(plan) - s3_url = upload_dpr_to_s3(doc, plan.id, plan.plan) - - dpr_report.dpr_report_s3_url = s3_url - dpr_report.dpr_generated_at = timezone.now() - dpr_report.status = "COMPLETED" - dpr_report.last_updated_at = timezone.now() - dpr_report.save(update_fields=[ - "dpr_report_s3_url", "dpr_generated_at", "status", "last_updated_at" - ]) - - logger.info(f"DPR generated and saved to S3: {s3_url}") - return dpr_report, True - - -@app.task(bind=True, name="dpr.generate_dpr_task") -def generate_dpr_task(self, plan_id: int, email_id: str, regenerate: bool = False): - plan = get_plan_details(plan_id) - if plan is None: - logger.error(f"Plan not found for ID: {plan_id}") - return {"error": "Plan not found"} - - dpr_report, was_regenerated = get_or_generate_dpr(plan, regenerate=regenerate) - mws_Ids = get_mws_ids_for_report(plan) - - mws_reports = [] - successful_mws_ids = [] - - state = transform_name(str(plan.state_soi.state_name)) - district = transform_name(str(plan.district_soi.district_name)) - block = transform_name(str(plan.tehsil_soi.tehsil_name)) - - for ids in mws_Ids: - try: - report_url = ( - f"https://geoserver.core-stack.org/api/v1/download_report/" - f"?report_type=mws&state={state}&district={district}&block={block}&uid={ids}" - ) - mws_reports.append(report_url) - successful_mws_ids.append(ids) - except Exception as e: - logger.error(f"Failed to generate MWS report for ID {ids}: {e}") - - # Fetch Resource Report PDF - resource_report_url = ( - f"https://geoserver.core-stack.org/api/v1/download_report/" - f"?report_type=resource&district={district}&block={block}&plan_id={plan_id}&plan_name={plan.plan}" - ) - - resource_report = None - try: - response = requests.get(resource_report_url, timeout=30) - response.raise_for_status() - resource_report = response.content - except Exception as e: - logger.error(f"Failed to fetch resource report: {e}") - - send_dpr_email( - email_id=email_id, - plan_name=plan.plan, - mws_reports=mws_reports, - mws_Ids=successful_mws_ids, - resource_report=resource_report, - resource_report_url=resource_report_url, - dpr_s3_url=dpr_report.dpr_report_s3_url, - state_name=plan.state_soi.state_name, - district_name=plan.district_soi.district_name, - tehsil_name=plan.tehsil_soi.tehsil_name, - ) - - return { - "status": "success", - "email_id": email_id, - "plan_id": plan_id, - "s3_url": dpr_report.dpr_report_s3_url, - "was_regenerated": was_regenerated, - } +from django.templatetags.i18n import language +from django.utils import timezone +from nrm_app.celery import app +from utilities.logger import setup_logger +import requests + +from .gen_dpr import ( + create_dpr_document, + get_mws_ids_for_report, + get_plan_details, +) +from .models import DPR_Report +from .utils import ( + transform_name, + send_dpr_email, + upload_dpr_to_s3, + check_dpr_exists_on_s3, +) + +# from .views import generate_dpr_pdf + +logger = setup_logger(__name__) + + +def get_or_generate_dpr(plan, regenerate=False): + dpr_report, created = DPR_Report.objects.get_or_create( + plan_id=plan, defaults={"plan_name": plan.plan, "status": "PENDING"} + ) + + if not regenerate: + s3_exists = check_dpr_exists_on_s3(dpr_report.dpr_report_s3_url) + if not created and not dpr_report.needs_regeneration() and s3_exists: + logger.info( + f"Using cached DPR for plan {plan.id} from S3: {dpr_report.dpr_report_s3_url}" + ) + return dpr_report, False + + logger.info(f"Generating new DPR for plan {plan.id}") + dpr_report.status = "GENERATING" + dpr_report.save(update_fields=["status"]) + + doc = create_dpr_document(plan) + s3_url = upload_dpr_to_s3(doc, plan.id, plan.plan) + + dpr_report.dpr_report_s3_url = s3_url + dpr_report.dpr_generated_at = timezone.now() + dpr_report.status = "COMPLETED" + dpr_report.last_updated_at = timezone.now() + dpr_report.save( + update_fields=[ + "dpr_report_s3_url", + "dpr_generated_at", + "status", + "last_updated_at", + ] + ) + + logger.info(f"DPR generated and saved to S3: {s3_url}") + return dpr_report, True + + +@app.task(bind=True, name="dpr.generate_dpr_task") +def generate_dpr_task(self, plan_id: int, email_id: str, regenerate: bool = False): + plan = get_plan_details(plan_id) + if plan is None: + logger.error(f"Plan not found for ID: {plan_id}") + return {"error": "Plan not found"} + + dpr_report, was_regenerated = get_or_generate_dpr(plan, regenerate=regenerate) + mws_Ids = get_mws_ids_for_report(plan) + + mws_reports = [] + successful_mws_ids = [] + + state = transform_name(str(plan.state_soi.state_name)) + district = transform_name(str(plan.district_soi.district_name)) + block = transform_name(str(plan.tehsil_soi.tehsil_name)) + + for ids in mws_Ids: + try: + report_url = ( + f"https://geoserver.core-stack.org/api/v1/download_report/" + f"?report_type=mws&state={state}&district={district}&block={block}&uid={ids}" + ) + mws_reports.append(report_url) + successful_mws_ids.append(ids) + except Exception as e: + logger.error(f"Failed to generate MWS report for ID {ids}: {e}") + + # Fetch Resource Report PDF + resource_report_url = ( + f"https://geoserver.core-stack.org/api/v1/download_report/" + f"?report_type=resource&district={district}&block={block}&plan_id={plan_id}&plan_name={plan.plan}" + ) + + resource_report = None + try: + response = requests.get(resource_report_url, timeout=30) + response.raise_for_status() + resource_report = response.content + except Exception as e: + logger.error(f"Failed to fetch resource report: {e}") + + send_dpr_email( + email_id=email_id, + plan_name=plan.plan, + mws_reports=mws_reports, + mws_Ids=successful_mws_ids, + resource_report=resource_report, + resource_report_url=resource_report_url, + dpr_s3_url=dpr_report.dpr_report_s3_url, + state_name=plan.state_soi.state_name, + district_name=plan.district_soi.district_name, + tehsil_name=plan.tehsil_soi.tehsil_name, + ) + + return { + "status": "success", + "email_id": email_id, + "plan_id": plan_id, + "s3_url": dpr_report.dpr_report_s3_url, + "was_regenerated": was_regenerated, + } diff --git a/dpr/templatetags/__init__.py b/dpr/templatetags/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/dpr/templatetags/custom_filters.py b/dpr/templatetags/custom_filters.py new file mode 100644 index 00000000..2d191c3f --- /dev/null +++ b/dpr/templatetags/custom_filters.py @@ -0,0 +1,11 @@ +from django import template + +register = template.Library() + + +@register.filter +def format_text(value): + if not value: + return "NA" + + return str(value).replace("_", " ") diff --git a/dpr/urls.py b/dpr/urls.py index 2b2e51a0..8cbc6779 100644 --- a/dpr/urls.py +++ b/dpr/urls.py @@ -1,6 +1,7 @@ from django.urls import path from . import api +from . import views urlpatterns = [ path( @@ -21,9 +22,16 @@ name="generate_resource_report", ), path("download_report/", api.download_report, name="download_report"), - path("generate_tehsil_report/", api.generate_tehsil_report, name="generate_tehsil_report"), - path("generate_village_report/", api.generate_village_report, name="generate_village_report"), - + path( + "generate_tehsil_report/", + api.generate_tehsil_report, + name="generate_tehsil_report", + ), + path( + "generate_village_report/", + api.generate_village_report, + name="generate_village_report", + ), # DPR Data API path("dpr_data//summary/", api.dpr_summary, name="dpr_summary"), path( diff --git a/dpr/utils.py b/dpr/utils.py index 07fe2529..669e9ee3 100644 --- a/dpr/utils.py +++ b/dpr/utils.py @@ -1,524 +1,540 @@ -import json -import re -import ssl -import socket -from io import BytesIO -import warnings -import time -from urllib.parse import urlparse -import pytz -import requests -from django.db.models import Max -from django.utils import timezone -from django.core.mail import EmailMessage -from django.core.mail.backends.smtp import EmailBackend -from docx import Document - -from nrm_app.settings import EMAIL_HOST, EMAIL_HOST_PASSWORD, EMAIL_HOST_USER, EMAIL_PORT, EMAIL_TIMEOUT, EMAIL_USE_SSL, ODK_PASSWORD, ODK_USERNAME -from utilities.constants import ( - ODK_URL_AGRI_MAINTENANCE, - ODK_URL_GW_MAINTENANCE, - ODK_URL_RS_WATERBODY_MAINTENANCE, - ODK_URL_WATERBODY_MAINTENANCE, - ODK_URL_agri, - ODK_URL_crop, - ODK_URL_gw, - ODK_URL_livelihood, - ODK_URL_settlement, - ODK_URL_waterbody, - ODK_URL_well, -) -from utilities.logger import setup_logger - -from .models import ( - Agri_maintenance, - GW_maintenance, - ODK_agri, - ODK_crop, - ODK_groundwater, - ODK_livelihood, - ODK_settlement, - ODK_waterbody, - ODK_well, - SWB_maintenance, - SWB_RS_maintenance, -) - -import boto3 -from nrm_app.settings import DPR_S3_ACCESS_KEY, DPR_S3_SECRET_KEY, DPR_S3_REGION, DPR_S3_BUCKET, DPR_S3_FOLDER -from botocore.exceptions import ClientError - -warnings.filterwarnings("ignore") - -logger = setup_logger(__name__) - - -def get_url(geoserver_url, workspace, layer_name): - """Construct the GeoServer WFS request URL for fetching GeoJSON data.""" - geojson_url = f"{geoserver_url}/{workspace}/ows?service=WFS&version=1.0.0&request=GetFeature&typeName={workspace}:{layer_name}&outputFormat=application/json" - return geojson_url - - -def get_vector_layer_geoserver(geoserver_url, workspace, layer_name): - """Fetch vector layer data from GeoServer and return as GeoJSON.""" - url = get_url(geoserver_url, workspace, layer_name) - try: - response = requests.get(url) - response.raise_for_status() - - # Check if the response content is not empty and is valid JSON - if response.content: - return response.json() - else: - print(f"Empty response for layer '{layer_name}'.") - return None - - except requests.exceptions.RequestException as e: - print(f"Failed to fetch the vector layer '{layer_name}' from GeoServer: {e}") - print(f"Request URL: {url}") - if response is not None: - print(f"Response status code: {response.status_code}") - # print(f"Response content: {response.text}") - return None - - -def determine_caste_fields(record): - """ - Determine caste group whether it's a Single Caste Group or Mixed Caste Group - """ - count_sc = record.get("count_sc") - count_st = record.get("count_st") - count_obc = record.get("count_obc") - count_general = record.get("count_general") - - caste_counts_mapping = { - "SC": count_sc, - "ST": count_st, - "OBC": count_obc, - "GENERAL": count_general, - } - - valid_castes = [] - for caste, count in caste_counts_mapping.items(): - if count is not None and count != "": - try: - count_value = float(count) if isinstance(count, str) else count - if count_value > 0: - valid_castes.append(caste) - except (ValueError, TypeError): - if count: - valid_castes.append(caste) - - if not valid_castes: - return None, None, None, True - - if len(valid_castes) == 1: - largest_caste = "Single Caste Group" - smallest_caste = valid_castes[0] - settlement_status = "NA" - return largest_caste, smallest_caste, settlement_status, False - - else: - largest_caste = "Mixed Caste Group" - smallest_caste = "NA" - settlement_status = ", ".join(sorted(valid_castes)) - return largest_caste, smallest_caste, settlement_status, False - - -def validate_email(emailid): - email_regex = r"^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$" - if not re.match(email_regex, emailid): - return False - else: - return True - - -def check_submission_time(record, model): - submission_date = timezone.datetime.strptime( - record.get("__system", {}).get("submissionDate", ""), "%Y-%m-%dT%H:%M:%S.%fZ" - ) - submission_date = timezone.make_aware(submission_date, pytz.UTC) - latest_submission_time = model.objects.aggregate(Max("submission_time"))[ - "submission_time__max" - ] - - if latest_submission_time and submission_date <= latest_submission_time: - return False, True - return True, False - - -def extract_coordinates(record): - try: - gps_point = record.get("GPS_point", {}) - if not gps_point: - return None, None - - # Check for both possible key names - maps_appearance = gps_point.get("point_mapsappearance") or gps_point.get( - "point_mapappearance" - ) - if not maps_appearance: - return None, None - - coordinates = maps_appearance.get("coordinates", []) - if len(coordinates) < 2: - return None, None - - return coordinates[1], coordinates[0] # latitude, longitude - except (AttributeError, IndexError, TypeError): - return None, None - - -def format_text_demands(text): - """ - Helps in converting demands in proper text - """ - if not text: - return "" - - items = text.split() - formatted_items = [] - - for item in items: - item_with_spaces = item.replace("_", " ") - formatted_item = " ".join( - word.capitalize() for word in item_with_spaces.split() - ) - formatted_items.append(formatted_item) - - formatted_text = "\n".join(formatted_items) - return formatted_text - - -def ensure_str(value): - """Normalize a value that may be a list (from Kobo multi-select fields) into a string.""" - if isinstance(value, list): - return " ".join(str(v) for v in value) - return value if value is not None else "" - - -def format_text(text): - """ - Converts text with underscores to properly formatted text. - Example: 'Delayed_payments_for_works' -> 'Delayed Payments For Works' - """ - if not text: - return "" - - text = ensure_str(text) - formatted_text = text.replace("_", " ") - return formatted_text.capitalize() + "\n\n" - - -def get_waterbody_repair_activities(data_waterbody, water_structure_type): - """ - Extract repair activities based on water structure type from data_waterbody. - Handles 'other' cases where the specific repair activity is in a separate field. - - Args: - data_waterbody (dict): The nested waterbody data dictionary - water_structure_type (str): The type of water structure - - Returns: - str: The repair activities or "NA" if none found - """ - if not data_waterbody or not water_structure_type: - return "NA" - - structure_type_mapping = { - "canal": "Repair_of_canal", - "bunding": "Repair_of_bunding", - "check dam": "Repair_of_check_dam", - "farm bund": "Repair_of_farm_bund", - "farm pond": "Repair_of_farm_ponds", - "soakage pits": "Repair_of_soakage_pits", - "recharge pits": "Repair_of_recharge_pits", - "rock fill dam": "Repair_of_rock_fill_dam", - "stone bunding": "Repair_of_stone_bunding", - "community pond": "Repair_of_community_pond", - "diversion drains": "Repair_of_diversion_drains", - "large water body": "Repair_of_large_water_body", - "model5 structure": "Repair_of_model5_structure", - "percolation tank": "Repair_of_percolation_tank", - "earthen gully plug": "Repair_of_earthen_gully_plug", - "30-40 model structure": "Repair_of_30_40_model_structure", - "loose boulder structure": "Repair_of_loose_boulder_structure", - "trench cum bund network": "Repair_of_trench_cum_bund_network", - "water absorption trenches": "Repair_of_Water_absorption_trenches", - "drainage soakage channels": "Repair_of_drainage_soakage_channels", - "staggered contour trenches": "Repair_of_Staggered_contour_trenches", - "continuous contour trenches": "Repair_of_Continuous_contour_trenches", - } - - structure_type_lower = water_structure_type.lower().strip() - if structure_type_lower.startswith("other:"): - repair_fields = [ - key for key in data_waterbody.keys() if key.startswith("Repair_of_") - ] - for field in repair_fields: - if data_waterbody.get(field): - repair_value = ensure_str(data_waterbody.get(field)) - other_field = field + "_other" - if ( - repair_value - and repair_value.lower() == "other" - and data_waterbody.get(other_field) - ): - return f"Other: {data_waterbody.get(other_field)}" - elif repair_value: - return repair_value.replace("_", " ").title() - return "NA" - - repair_field = structure_type_mapping.get(structure_type_lower) - - if not repair_field: - return "NA" - - repair_activity = ensure_str(data_waterbody.get(repair_field)) - - if not repair_activity: - return "NA" - - if repair_activity.lower() == "other": - other_field = repair_field + "_other" - other_value = data_waterbody.get(other_field) - if other_value: - return f"Other: {other_value}" - else: - return "Other" - - return repair_activity.replace("_", " ").title() - - -def sort_key(settlement): - return (settlement == "NA", settlement.lower() if settlement != "NA" else "") - - -def transform_name(name): - if not name: - return name - - name = re.sub(r"[()]", "", name) - name = re.sub(r"[-\s]+", "_", name) - name = re.sub(r"_+", "_", name) - name = re.sub(r"^_|_$", "", name) - return name.lower() - -def to_utf8(value): - """Ensure value is a properly encoded UTF-8 string for Word document. - - Handles cases where UTF-8 text was incorrectly decoded as Latin-1, - resulting in garbled characters like 'ಪಾà²...' for Kannada/Hindi text. - """ - if value is None: - return "NA" - if isinstance(value, list): - value = " ".join(str(v) for v in value) - if isinstance(value, bytes): - try: - return value.decode('utf-8') - except UnicodeDecodeError: - return value.decode('latin-1') - if not isinstance(value, str): - value = str(value) - try: - return value.encode('latin-1').decode('utf-8') - except (UnicodeDecodeError, UnicodeEncodeError): - return value - - -def send_dpr_email( - email_id, - plan_name, - mws_reports, - mws_Ids, - resource_report, - resource_report_url, - dpr_s3_url, - state_name="", - district_name="", - tehsil_name="", -): - try: - mws_table_html = "" - if mws_reports and mws_Ids: - mws_rows = "".join( - f'{mws_id}' - f'' - f'View Report' - for mws_id, report_url in zip(mws_Ids, mws_reports) - ) - mws_table_html = f""" -
-

MWS Reports

- - - - - - - - {mws_rows} -
MWS IDReport
-
- """ - - email_body = f""" - - - - -
-
-
-

Detailed Project Report

-

{to_utf8(plan_name)}

-

{to_utf8(tehsil_name)} · {to_utf8(district_name)} · {to_utf8(state_name)}

-
-
-

- Hi,

- Your Detailed Project Report for {to_utf8(plan_name)} is ready. -

-
-

Download DPR Report

- Download DPR → -
- {mws_table_html} -
-

Resource Report

- View Report → -
-
-
-

- Thanks and Regards,
- CoRE Stack Team -

-
-
-

- This is an automated email from CoRE Stack. -

-
- - - """ - - backend = EmailBackend( - host=EMAIL_HOST, - port=EMAIL_PORT, - username=EMAIL_HOST_USER, - password=EMAIL_HOST_PASSWORD, - use_ssl=EMAIL_USE_SSL, - timeout=EMAIL_TIMEOUT, - ssl_context=ssl.create_default_context(), - ) - - email = EmailMessage( - subject=f"DPR of plan: {plan_name}", - body=email_body, - from_email=EMAIL_HOST_USER, - to=[email_id], - connection=backend, - ) - - email.content_subtype = "html" - - if resource_report is not None: - email.attach( - f"Resource Report_{plan_name}.pdf", resource_report, "application/pdf" - ) - - logger.info("Sending DPR email to %s", email_id) - email.send(fail_silently=False) - logger.info("DPR email sent.") - backend.close() - - except socket.error as e: - logger.error(f"Socket error: {e}") - except ssl.SSLError as e: - logger.error(f"SSL error: {e}") - except Exception as e: - logger.error(f"Failed to send email: {e}") - - -def upload_dpr_to_s3(doc, plan_id, plan_name): - doc_bytes = BytesIO() - doc.save(doc_bytes) - doc_bytes.seek(0) - - safe_plan_name = transform_name(plan_name) - s3_key = f"{DPR_S3_FOLDER}/{plan_id}_{safe_plan_name}.docx" - - s3_client = boto3.client( - "s3", - aws_access_key_id=DPR_S3_ACCESS_KEY, - aws_secret_access_key=DPR_S3_SECRET_KEY, - region_name=DPR_S3_REGION, - ) - - s3_client.upload_fileobj( - doc_bytes, - DPR_S3_BUCKET, - s3_key, - ExtraArgs={ - "ContentType": "application/vnd.openxmlformats-officedocument.wordprocessingml.document", - "ContentDisposition": f'attachment; filename="DPR_{safe_plan_name}.docx"', - "CacheControl": "no-cache, no-store, must-revalidate", - } - ) - - ts = int(time.time()) - s3_url = f"https://{DPR_S3_BUCKET}.s3.{DPR_S3_REGION}.amazonaws.com/{s3_key}?v={ts}" - logger.info(f"DPR uploaded to S3: {s3_url}") - return s3_url - - -def _extract_s3_key(s3_url): - - parsed = urlparse(s3_url) - return parsed.path.lstrip("/") - - -def check_dpr_exists_on_s3(s3_url): - if not s3_url: - return False - - try: - s3_key = _extract_s3_key(s3_url) - except (IndexError, AttributeError): - return False - - s3_client = boto3.client( - "s3", - aws_access_key_id=DPR_S3_ACCESS_KEY, - aws_secret_access_key=DPR_S3_SECRET_KEY, - region_name=DPR_S3_REGION, - ) - - try: - s3_client.head_object(Bucket=DPR_S3_BUCKET, Key=s3_key) - return True - except ClientError: - logger.warning(f"DPR not found on S3: {s3_url}") - return False - - -def download_dpr_from_s3(s3_url): - s3_key = _extract_s3_key(s3_url) - - s3_client = boto3.client( - "s3", - aws_access_key_id=DPR_S3_ACCESS_KEY, - aws_secret_access_key=DPR_S3_SECRET_KEY, - region_name=DPR_S3_REGION, - ) - - doc_bytes = BytesIO() - s3_client.download_fileobj(DPR_S3_BUCKET, s3_key, doc_bytes) - doc_bytes.seek(0) - - doc = Document(doc_bytes) - logger.info(f"DPR downloaded from S3: {s3_url}") - return doc \ No newline at end of file +import json +import re +import ssl +import socket +from io import BytesIO +import warnings +import time +from urllib.parse import urlparse +import pytz +import requests +from django.db.models import Max +from django.utils import timezone +from django.core.mail import EmailMessage +from django.core.mail.backends.smtp import EmailBackend +from docx import Document + +from nrm_app.settings import ( + EMAIL_HOST, + EMAIL_HOST_PASSWORD, + EMAIL_HOST_USER, + EMAIL_PORT, + EMAIL_TIMEOUT, + EMAIL_USE_SSL, + ODK_PASSWORD, + ODK_USERNAME, +) +from utilities.constants import ( + ODK_URL_AGRI_MAINTENANCE, + ODK_URL_GW_MAINTENANCE, + ODK_URL_RS_WATERBODY_MAINTENANCE, + ODK_URL_WATERBODY_MAINTENANCE, + ODK_URL_agri, + ODK_URL_crop, + ODK_URL_gw, + ODK_URL_livelihood, + ODK_URL_settlement, + ODK_URL_waterbody, + ODK_URL_well, +) +from utilities.logger import setup_logger + +from .models import ( + Agri_maintenance, + GW_maintenance, + ODK_agri, + ODK_crop, + ODK_groundwater, + ODK_livelihood, + ODK_settlement, + ODK_waterbody, + ODK_well, + SWB_maintenance, + SWB_RS_maintenance, +) + +import boto3 +from nrm_app.settings import ( + DPR_S3_ACCESS_KEY, + DPR_S3_SECRET_KEY, + DPR_S3_REGION, + DPR_S3_BUCKET, + DPR_S3_FOLDER, +) +from botocore.exceptions import ClientError + +warnings.filterwarnings("ignore") + +logger = setup_logger(__name__) + + +def get_url(geoserver_url, workspace, layer_name): + """Construct the GeoServer WFS request URL for fetching GeoJSON data.""" + geojson_url = f"{geoserver_url}/{workspace}/ows?service=WFS&version=1.0.0&request=GetFeature&typeName={workspace}:{layer_name}&outputFormat=application/json" + return geojson_url + + +def get_vector_layer_geoserver(geoserver_url, workspace, layer_name): + """Fetch vector layer data from GeoServer and return as GeoJSON.""" + url = get_url(geoserver_url, workspace, layer_name) + try: + response = requests.get(url) + response.raise_for_status() + + # Check if the response content is not empty and is valid JSON + if response.content: + return response.json() + else: + print(f"Empty response for layer '{layer_name}'.") + return None + + except requests.exceptions.RequestException as e: + print(f"Failed to fetch the vector layer '{layer_name}' from GeoServer: {e}") + print(f"Request URL: {url}") + if response is not None: + print(f"Response status code: {response.status_code}") + # print(f"Response content: {response.text}") + return None + + +def determine_caste_fields(record): + """ + Determine caste group whether it's a Single Caste Group or Mixed Caste Group + """ + count_sc = record.get("count_sc") + count_st = record.get("count_st") + count_obc = record.get("count_obc") + count_general = record.get("count_general") + + caste_counts_mapping = { + "SC": count_sc, + "ST": count_st, + "OBC": count_obc, + "GENERAL": count_general, + } + + valid_castes = [] + for caste, count in caste_counts_mapping.items(): + if count is not None and count != "": + try: + count_value = float(count) if isinstance(count, str) else count + if count_value > 0: + valid_castes.append(caste) + except (ValueError, TypeError): + if count: + valid_castes.append(caste) + + if not valid_castes: + return None, None, None, True + + if len(valid_castes) == 1: + largest_caste = "Single Caste Group" + smallest_caste = valid_castes[0] + settlement_status = "NA" + return largest_caste, smallest_caste, settlement_status, False + + else: + largest_caste = "Mixed Caste Group" + smallest_caste = "NA" + settlement_status = ", ".join(sorted(valid_castes)) + return largest_caste, smallest_caste, settlement_status, False + + +def validate_email(emailid): + email_regex = r"^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$" + if not re.match(email_regex, emailid): + return False + else: + return True + + +def check_submission_time(record, model): + submission_date = timezone.datetime.strptime( + record.get("__system", {}).get("submissionDate", ""), "%Y-%m-%dT%H:%M:%S.%fZ" + ) + submission_date = timezone.make_aware(submission_date, pytz.UTC) + latest_submission_time = model.objects.aggregate(Max("submission_time"))[ + "submission_time__max" + ] + + if latest_submission_time and submission_date <= latest_submission_time: + return False, True + return True, False + + +def extract_coordinates(record): + try: + gps_point = record.get("GPS_point", {}) + if not gps_point: + return None, None + + # Check for both possible key names + maps_appearance = gps_point.get("point_mapsappearance") or gps_point.get( + "point_mapappearance" + ) + if not maps_appearance: + return None, None + + coordinates = maps_appearance.get("coordinates", []) + if len(coordinates) < 2: + return None, None + + return coordinates[1], coordinates[0] # latitude, longitude + except (AttributeError, IndexError, TypeError): + return None, None + + +def format_text_demands(text): + """ + Helps in converting demands in proper text + """ + if not text: + return "" + + items = text.split() + formatted_items = [] + + for item in items: + item_with_spaces = item.replace("_", " ") + formatted_item = " ".join( + word.capitalize() for word in item_with_spaces.split() + ) + formatted_items.append(formatted_item) + + formatted_text = "\n".join(formatted_items) + return formatted_text + + +def ensure_str(value): + """Normalize a value that may be a list (from Kobo multi-select fields) into a string.""" + if isinstance(value, list): + return " ".join(str(v) for v in value) + return value if value is not None else "" + + +def format_text(text): + """ + Converts text with underscores to properly formatted text. + Example: 'Delayed_payments_for_works' -> 'Delayed Payments For Works' + """ + if not text: + return "" + + text = ensure_str(text) + formatted_text = text.replace("_", " ") + return formatted_text.capitalize() + "\n\n" + + +def get_waterbody_repair_activities(data_waterbody, water_structure_type): + """ + Extract repair activities based on water structure type from data_waterbody. + Handles 'other' cases where the specific repair activity is in a separate field. + + Args: + data_waterbody (dict): The nested waterbody data dictionary + water_structure_type (str): The type of water structure + + Returns: + str: The repair activities or "NA" if none found + """ + if not data_waterbody or not water_structure_type: + return "NA" + + structure_type_mapping = { + "canal": "Repair_of_canal", + "bunding": "Repair_of_bunding", + "check dam": "Repair_of_check_dam", + "farm bund": "Repair_of_farm_bund", + "farm pond": "Repair_of_farm_ponds", + "soakage pits": "Repair_of_soakage_pits", + "recharge pits": "Repair_of_recharge_pits", + "rock fill dam": "Repair_of_rock_fill_dam", + "stone bunding": "Repair_of_stone_bunding", + "community pond": "Repair_of_community_pond", + "diversion drains": "Repair_of_diversion_drains", + "large water body": "Repair_of_large_water_body", + "model5 structure": "Repair_of_model5_structure", + "percolation tank": "Repair_of_percolation_tank", + "earthen gully plug": "Repair_of_earthen_gully_plug", + "30-40 model structure": "Repair_of_30_40_model_structure", + "loose boulder structure": "Repair_of_loose_boulder_structure", + "trench cum bund network": "Repair_of_trench_cum_bund_network", + "water absorption trenches": "Repair_of_Water_absorption_trenches", + "drainage soakage channels": "Repair_of_drainage_soakage_channels", + "staggered contour trenches": "Repair_of_Staggered_contour_trenches", + "continuous contour trenches": "Repair_of_Continuous_contour_trenches", + } + + structure_type_lower = water_structure_type.lower().strip() + if structure_type_lower.startswith("other:"): + repair_fields = [ + key for key in data_waterbody.keys() if key.startswith("Repair_of_") + ] + for field in repair_fields: + if data_waterbody.get(field): + repair_value = ensure_str(data_waterbody.get(field)) + other_field = field + "_other" + if ( + repair_value + and repair_value.lower() == "other" + and data_waterbody.get(other_field) + ): + return f"Other: {data_waterbody.get(other_field)}" + # elif repair_value: + # return repair_value.replace("_", " ").title() + return "NA" + + repair_field = structure_type_mapping.get(structure_type_lower) + + if not repair_field: + return "NA" + + repair_activity = ensure_str(data_waterbody.get(repair_field)) + + if not repair_activity: + return "NA" + + if repair_activity.lower() == "other": + other_field = repair_field + "_other" + other_value = data_waterbody.get(other_field) + if other_value: + return f"Other: {other_value}" + else: + return "Other" + + return repair_activity + + +def sort_key(settlement): + return (settlement == "NA", settlement.lower() if settlement != "NA" else "") + + +def transform_name(name): + if not name: + return name + + name = re.sub(r"[()]", "", name) + name = re.sub(r"[-\s]+", "_", name) + name = re.sub(r"_+", "_", name) + name = re.sub(r"^_|_$", "", name) + return name.lower() + + +def to_utf8(value): + """Ensure value is a properly encoded UTF-8 string for Word document. + + Handles cases where UTF-8 text was incorrectly decoded as Latin-1, + resulting in garbled characters like 'ಪಾà²...' for Kannada/Hindi text. + """ + if value is None: + return "NA" + if isinstance(value, list): + value = " ".join(str(v) for v in value) + if isinstance(value, bytes): + try: + return value.decode("utf-8") + except UnicodeDecodeError: + return value.decode("latin-1") + if not isinstance(value, str): + value = str(value) + try: + return value.encode("latin-1").decode("utf-8") + except (UnicodeDecodeError, UnicodeEncodeError): + return value + + +def send_dpr_email( + email_id, + plan_name, + mws_reports, + mws_Ids, + resource_report, + resource_report_url, + dpr_s3_url, + state_name="", + district_name="", + tehsil_name="", +): + try: + mws_table_html = "" + if mws_reports and mws_Ids: + mws_rows = "".join( + f'{mws_id}' + f'' + f'View Report' + for mws_id, report_url in zip(mws_Ids, mws_reports) + ) + mws_table_html = f""" +
+

MWS Reports

+ + + + + + + + {mws_rows} +
MWS IDReport
+
+ """ + + email_body = f""" + + + + +
+
+
+

Detailed Project Report

+

{to_utf8(plan_name)}

+

{to_utf8(tehsil_name)} · {to_utf8(district_name)} · {to_utf8(state_name)}

+
+
+

+ Hi,

+ Your Detailed Project Report for {to_utf8(plan_name)} is ready. +

+
+

Download DPR Report

+ Download DPR → +
+ {mws_table_html} +
+

Resource Report

+ View Report → +
+
+
+

+ Thanks and Regards,
+ CoRE Stack Team +

+
+
+

+ This is an automated email from CoRE Stack. +

+
+ + + """ + + backend = EmailBackend( + host=EMAIL_HOST, + port=EMAIL_PORT, + username=EMAIL_HOST_USER, + password=EMAIL_HOST_PASSWORD, + use_ssl=EMAIL_USE_SSL, + timeout=EMAIL_TIMEOUT, + ssl_context=ssl.create_default_context(), + ) + + email = EmailMessage( + subject=f"DPR of plan: {plan_name}", + body=email_body, + from_email=EMAIL_HOST_USER, + to=[email_id], + connection=backend, + ) + + email.content_subtype = "html" + + if resource_report is not None: + email.attach( + f"Resource Report_{plan_name}.pdf", resource_report, "application/pdf" + ) + + logger.info("Sending DPR email to %s", email_id) + email.send(fail_silently=False) + logger.info("DPR email sent.") + backend.close() + + except socket.error as e: + logger.error(f"Socket error: {e}") + except ssl.SSLError as e: + logger.error(f"SSL error: {e}") + except Exception as e: + logger.error(f"Failed to send email: {e}") + + +def upload_dpr_to_s3(doc, plan_id, plan_name): + doc_bytes = BytesIO() + doc.save(doc_bytes) + doc_bytes.seek(0) + + safe_plan_name = transform_name(plan_name) + s3_key = f"{DPR_S3_FOLDER}/{plan_id}_{safe_plan_name}.docx" + + s3_client = boto3.client( + "s3", + aws_access_key_id=DPR_S3_ACCESS_KEY, + aws_secret_access_key=DPR_S3_SECRET_KEY, + region_name=DPR_S3_REGION, + ) + + s3_client.upload_fileobj( + doc_bytes, + DPR_S3_BUCKET, + s3_key, + ExtraArgs={ + "ContentType": "application/vnd.openxmlformats-officedocument.wordprocessingml.document", + "ContentDisposition": f'attachment; filename="DPR_{safe_plan_name}.docx"', + "CacheControl": "no-cache, no-store, must-revalidate", + }, + ) + + ts = int(time.time()) + s3_url = f"https://{DPR_S3_BUCKET}.s3.{DPR_S3_REGION}.amazonaws.com/{s3_key}?v={ts}" + logger.info(f"DPR uploaded to S3: {s3_url}") + return s3_url + + +def _extract_s3_key(s3_url): + + parsed = urlparse(s3_url) + return parsed.path.lstrip("/") + + +def check_dpr_exists_on_s3(s3_url): + if not s3_url: + return False + + try: + s3_key = _extract_s3_key(s3_url) + except (IndexError, AttributeError): + return False + + s3_client = boto3.client( + "s3", + aws_access_key_id=DPR_S3_ACCESS_KEY, + aws_secret_access_key=DPR_S3_SECRET_KEY, + region_name=DPR_S3_REGION, + ) + + try: + s3_client.head_object(Bucket=DPR_S3_BUCKET, Key=s3_key) + return True + except ClientError: + logger.warning(f"DPR not found on S3: {s3_url}") + return False + + +def download_dpr_from_s3(s3_url): + s3_key = _extract_s3_key(s3_url) + + s3_client = boto3.client( + "s3", + aws_access_key_id=DPR_S3_ACCESS_KEY, + aws_secret_access_key=DPR_S3_SECRET_KEY, + region_name=DPR_S3_REGION, + ) + + doc_bytes = BytesIO() + s3_client.download_fileobj(DPR_S3_BUCKET, s3_key, doc_bytes) + doc_bytes.seek(0) + + doc = Document(doc_bytes) + logger.info(f"DPR downloaded from S3: {s3_url}") + return doc diff --git a/dpr/views.py b/dpr/views.py index 91ea44a2..08a663ff 100644 --- a/dpr/views.py +++ b/dpr/views.py @@ -1,3 +1,64 @@ -from django.shortcuts import render - -# Create your views here. +from datetime import date +from django.template.loader import render_to_string +from dpr.service.translation_service import load_translations + +# from weasyprint import HTML +from .gen_dpr import get_settlement_count_for_plan +from .utils import get_vector_layer_geoserver, transform_name +from nrm_app.settings import GEOSERVER_URL +from .get_dpr_sectionwise_data import ( + get_section_b_data, + get_section_c_data, + get_section_d_data, + get_section_e_data, + get_section_f_data, + get_section_g_data, +) +from .service.form_download_service import sync_odk_forms + + +def generate_dpr_html(plan, language="en"): + translations = load_translations(language) + total_settlements = get_settlement_count_for_plan(plan.id) + mws_fortnight = get_vector_layer_geoserver( + geoserver_url=GEOSERVER_URL, + workspace="mws_layers", + layer_name="deltaG_fortnight_" + + transform_name(str(plan.district_soi.district_name)) + + "_" + + transform_name(str(plan.tehsil_soi.tehsil_name)), + ) + section_b_data, settlement_mws_ids, mws_gdf = get_section_b_data( + plan, total_settlements, mws_fortnight + ) + section_c_data = get_section_c_data(plan, language) + section_d_data = get_section_d_data(plan, settlement_mws_ids, mws_gdf, language) + section_e_data = get_section_e_data(plan, language) + section_f_data = get_section_f_data(plan, language) + section_g_data = get_section_g_data(plan, language) + html = render_to_string( + "dpr/base.html", + { + "t": translations, + "current_date": date.today().strftime("%B %d, %Y"), + "section_a": plan, + "section_b": section_b_data, + "section_c": section_c_data, + "section_d": section_d_data, + "section_e": section_e_data, + "section_f": section_f_data, + "section_g": section_g_data, + "footnote": f"DPR supported by {plan.organization.name} in {date.today().year}", + }, + ) + + return html + + +# def generate_dpr_pdf(plan, language="en"): +# sync_odk_forms() +# html = generate_dpr_html(plan, language) +# +# pdf = HTML(string=html).write_pdf() +# +# return pdf diff --git a/nrm_app/settings.py b/nrm_app/settings.py index 19a80786..0c057de8 100755 --- a/nrm_app/settings.py +++ b/nrm_app/settings.py @@ -352,7 +352,8 @@ def resolve_env_path(name, default="", *, trailing_sep=False): EMAIL_USE_TLS = False EMAIL_HOST_USER = env("EMAIL_HOST_USER") EMAIL_HOST_PASSWORD = env("EMAIL_HOST_PASSWORD") -EMAIL_TIMEOUT = 30 +EMAIL_TIMEOUT = 900 +MISSING_LAYER_RECIPIENTS = env.list("MISSING_LAYER_RECIPIENTS") PASSWORD_RESET_TIMEOUT = 259200 # 3 days in seconds GEOSERVER_URL = env("GEOSERVER_URL", default="") @@ -418,7 +419,7 @@ def resolve_env_path(name, default="", *, trailing_sep=False): FERNET_KEY = env("FERNET_KEY") API_KEY = env("API_KEY", default="") - +RECAPTCHA_SECRET_KEY = env("RECAPTCHA_SECRET_KEY", default="") lulc_years = [ "2017_2018", diff --git a/plans/api.py b/plans/api.py index 533a26f3..dbd8eca2 100644 --- a/plans/api.py +++ b/plans/api.py @@ -1,758 +1,789 @@ -import logging -import os -import time -import uuid -from typing import Any, Dict, List, Optional, Tuple - -import requests -from django.views.decorators.csrf import csrf_exempt -from rest_framework import status -from rest_framework.decorators import api_view, schema -from rest_framework.response import Response - -from dpr.utils import transform_name -from moderation.utils.update_csdb import sync_form_type -from nrm_app.settings import ODK_USER_EMAIL_SYNC, ODK_USER_PASSWORD_SYNC, TMP_LOCATION -from utilities.auth_check_decorator import api_security_check -from utilities.auth_utils import auth_free -from utilities.constants import ( - ODK_SYNC_URL_AGRI_FEEDBACK, - ODK_SYNC_URL_AGRI_MAINTENANCE, - ODK_SYNC_URL_AGROHORTICULTURE, - ODK_SYNC_URL_CROP, - ODK_SYNC_URL_GW_FEEDBACK, - ODK_SYNC_URL_GW_MAINTENANCE, - ODK_SYNC_URL_IRRIGATION_STRUCTURE, - ODK_SYNC_URL_LIVELIHOOD, - ODK_SYNC_URL_RECHARGE_STRUCTURE, - ODK_SYNC_URL_RS_WATERBODY_MAINTENANCE, - ODK_SYNC_URL_SETTLEMENT, - ODK_SYNC_URL_SWB_FEEDBACK, - ODK_SYNC_URL_WATER_STRUCTURES, - ODK_SYNC_URL_WATER_STRUCTURES_MAINTENANCE, - ODK_SYNC_URL_WELL, -) - -logger = logging.getLogger(__name__) - -from .build_layer import build_layer -from .models import ODKSyncLog, Plan -from .serializers import PlanAppSerializer -from .utils import fetch_bearer_token, fetch_db_data - -_COMMON_REQUIRED_FIELDS: Tuple[str, ...] = ( - "layer_name", - "plan_id", - "plan_name", - "district_name", - "block_name", -) - -_LAYER_KIND_CONFIG: Dict[str, Dict[str, str]] = { - "resources": {"type_field": "resource_type", "singular": "resource"}, - "works": {"type_field": "work_type", "singular": "work"}, -} - - -# MARK: Get Plans API -@api_security_check(auth_type="Auth_free") -@schema(None) -def get_plans(request): - """ - Get Plans API - - Args: - block_id (str, optional): Block ID. Defaults to None. - - Returns: - Response: JSON response containing a list of plans of a block or all the plans - """ - try: - block_id = request.query_params.get("block_id", None) - if block_id is not None: - plans = Plan.objects.filter(block=block_id) - else: - plans = Plan.objects.all() - serializer = PlanAppSerializer(plans, many=True) - response = {"plans": serializer.data} - - return Response(response, status=status.HTTP_200_OK) - except Exception as e: - print("Exception in get_plans api :: ", e) - return Response({"Exception": e}, status=status.HTTP_500_INTERNAL_SERVER_ERROR) - - -@api_view(["POST"]) -@auth_free -@schema(None) -def add_plan(request): - if request.method == "POST": - serializer = PlanAppSerializer(data=request.data) - if serializer.is_valid(): - serializer.save() # Save the new Plan instance if validation passes - return Response(serializer.data, status=status.HTTP_201_CREATED) - else: - return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) - return Response( - {"error": "Method not allowed"}, status=status.HTTP_405_METHOD_NOT_ALLOWED - ) - - -# MARK: Build Layer Helpers (shared by /add_resources and /add_works) -def _extract_payload(request, kind: str) -> Tuple[Optional[Dict[str, Any]], List[str]]: - """Pull and normalize the request payload. Returns (payload, missing_fields).""" - type_field = _LAYER_KIND_CONFIG[kind]["type_field"] - required = (*_COMMON_REQUIRED_FIELDS, type_field) - - missing = [f for f in required if not request.data.get(f)] - if missing: - return None, missing - - def _lower(value: Any) -> Any: - return value.lower() if isinstance(value, str) else value - - return { - "layer_name": _lower(request.data.get("layer_name")), - "item_type": _lower(request.data.get(type_field)), - "plan_id": request.data.get("plan_id"), - "plan_name": _lower(request.data.get("plan_name")), - "district": _lower(request.data.get("district_name")), - "block": _lower(request.data.get("block_name")), - }, [] - - -def _expected_layer_store_name( - item_type: str, plan_id: Any, district: str, block: str -) -> str: - """Mirror the naming convention used by build_layer.build_layer for transparency.""" - return f"{item_type}_{plan_id}_{district}_{transform_name(name=block)}" - - -def _safe_unlink(csv_path: str, request_id: str, kind: str) -> None: - try: - if os.path.exists(csv_path): - os.remove(csv_path) - logger.info( - f"[{request_id}] {kind}.build: cleaned up temp CSV at {csv_path}" - ) - except OSError as exc: - logger.warning( - f"[{request_id}] {kind}.build: failed to remove temp CSV at " - f"{csv_path}: {exc}" - ) - - -def _error_response( - request_id: str, - code: str, - message: str, - http_status: int, - extra: Optional[Dict[str, Any]] = None, -) -> Response: - data: Dict[str, Any] = {"request_id": request_id} - if extra: - data.update(extra) - return Response( - { - "status": "error", - "code": code, - "error": message, - "data": data, - }, - status=http_status, - ) - - -def _build_layer_for_kind(request, kind: str) -> Response: - """ - Shared workflow for /add_resources and /add_works: - 1. validate payload - 2. trigger incremental ODK -> DB sync (best-effort) - 3. fetch source records from DB and stage a CSV - 4. publish the layer to GeoServer - 5. clean up the temp CSV and return a structured response - """ - request_id = uuid.uuid4().hex[:12] - type_field = _LAYER_KIND_CONFIG[kind]["type_field"] - singular = _LAYER_KIND_CONFIG[kind]["singular"] - started_at = time.perf_counter() - - logger.info( - f"[{request_id}] {kind}.build: request received " - f"(content_type={request.content_type}, keys={list(request.data.keys())})" - ) - - payload, missing = _extract_payload(request, kind) - if missing: - logger.warning( - f"[{request_id}] {kind}.build: rejecting request — " - f"missing/empty fields: {missing}" - ) - return _error_response( - request_id, - code="missing_fields", - message=f"Missing required field(s): {', '.join(missing)}.", - http_status=status.HTTP_400_BAD_REQUEST, - extra={"missing_fields": missing}, - ) - - item_type = payload["item_type"] - plan_id = payload["plan_id"] - plan_name = payload["plan_name"] - district = payload["district"] - block = payload["block"] - layer_name = payload["layer_name"] - - context = { - type_field: item_type, - "plan_id": plan_id, - "plan_name": plan_name, - "district": district, - "block": block, - "layer_name": layer_name, - } - logger.info(f"[{request_id}] {kind}.build: payload normalized — {context}") - - csv_path = os.path.join(TMP_LOCATION, f"{item_type}_{plan_id}_{block}.csv") - logger.info(f"[{request_id}] {kind}.build: temp CSV path resolved to {csv_path}") - - sync_started = time.perf_counter() - logger.info( - f"[{request_id}] {kind}.build: triggering incremental ODK sync for " - f"{type_field}={item_type}" - ) - sync_ok = sync_form_type(item_type) - sync_ms = int((time.perf_counter() - sync_started) * 1000) - if sync_ok: - logger.info( - f"[{request_id}] {kind}.build: ODK sync completed for " - f"{type_field}={item_type} in {sync_ms}ms" - ) - else: - logger.warning( - f"[{request_id}] {kind}.build: ODK sync FAILED for " - f"{type_field}={item_type} in {sync_ms}ms; proceeding with existing DB data" - ) - - fetch_started = time.perf_counter() - logger.info( - f"[{request_id}] {kind}.build: fetching DB data for {type_field}={item_type}, " - f"plan_id={plan_id}, block={block}" - ) - try: - record_count = fetch_db_data(csv_path, item_type, block, plan_id) - except Exception as exc: - fetch_ms = int((time.perf_counter() - fetch_started) * 1000) - logger.exception( - f"[{request_id}] {kind}.build: unexpected error during fetch_db_data " - f"for {type_field}={item_type}, plan_id={plan_id} " - f"(fetch_ms={fetch_ms}): {exc}" - ) - _safe_unlink(csv_path, request_id, kind) - return _error_response( - request_id, - code="db_fetch_failed", - message="Failed to fetch source data from the database.", - http_status=status.HTTP_500_INTERNAL_SERVER_ERROR, - extra={ - **context, - "details": str(exc), - "sync_status": "success" if sync_ok else "failed", - "sync_duration_ms": sync_ms, - "fetch_duration_ms": fetch_ms, - }, - ) - fetch_ms = int((time.perf_counter() - fetch_started) * 1000) - - if not record_count: - total_ms = int((time.perf_counter() - started_at) * 1000) - logger.warning( - f"[{request_id}] {kind}.build: no DB data found for " - f"{type_field}={item_type}, plan_id={plan_id}, block={block} " - f"(sync_ok={sync_ok}, fetch_ms={fetch_ms}, total_ms={total_ms})" - ) - return _error_response( - request_id, - code="no_data_found", - message=( - f"No records found for {type_field}='{item_type}', " - f"plan_id='{plan_id}', block='{block}'." - ), - http_status=status.HTTP_404_NOT_FOUND, - extra={ - **context, - "record_count": 0, - "sync_status": "success" if sync_ok else "failed", - "sync_duration_ms": sync_ms, - "fetch_duration_ms": fetch_ms, - "total_duration_ms": total_ms, - }, - ) - logger.info( - f"[{request_id}] {kind}.build: DB fetch staged {record_count} row(s) " - f"in {fetch_ms}ms" - ) - - layer_store_name = _expected_layer_store_name(item_type, plan_id, district, block) - build_started = time.perf_counter() - logger.info( - f"[{request_id}] {kind}.build: publishing GeoServer layer " - f"workspace='{kind}', store='{layer_store_name}'" - ) - try: - success = build_layer( - layer_type=kind, - item_type=item_type, - plan_id=plan_id, - district=district, - block=block, - csv_path=csv_path, - ) - except Exception as exc: - build_ms = int((time.perf_counter() - build_started) * 1000) - total_ms = int((time.perf_counter() - started_at) * 1000) - logger.exception( - f"[{request_id}] {kind}.build: unexpected error during build_layer for " - f"{type_field}={item_type}, plan_id={plan_id} " - f"(build_ms={build_ms}, total_ms={total_ms}): {exc}" - ) - _safe_unlink(csv_path, request_id, kind) - return _error_response( - request_id, - code="internal_error", - message="An unexpected error occurred while building the layer.", - http_status=status.HTTP_500_INTERNAL_SERVER_ERROR, - extra={ - **context, - "layer_store_name": layer_store_name, - "details": str(exc), - "sync_status": "success" if sync_ok else "failed", - "sync_duration_ms": sync_ms, - "fetch_duration_ms": fetch_ms, - "build_duration_ms": build_ms, - "total_duration_ms": total_ms, - }, - ) - finally: - _safe_unlink(csv_path, request_id, kind) - - build_ms = int((time.perf_counter() - build_started) * 1000) - total_ms = int((time.perf_counter() - started_at) * 1000) - - if not success: - logger.error( - f"[{request_id}] {kind}.build: build_layer returned False for " - f"{type_field}={item_type}, plan_id={plan_id} " - f"(build_ms={build_ms}, total_ms={total_ms})" - ) - return _error_response( - request_id, - code="layer_build_failed", - message=( - f"Failed to publish GeoServer layer '{layer_store_name}'. " - "See server logs for details." - ), - http_status=status.HTTP_500_INTERNAL_SERVER_ERROR, - extra={ - **context, - "layer_store_name": layer_store_name, - "record_count": record_count, - "sync_status": "success" if sync_ok else "failed", - "sync_duration_ms": sync_ms, - "fetch_duration_ms": fetch_ms, - "build_duration_ms": build_ms, - "total_duration_ms": total_ms, - }, - ) - - logger.info( - f"[{request_id}] {kind}.build: SUCCESS — published layer " - f"'{layer_store_name}' ({record_count} row(s)) in workspace='{kind}' " - f"(sync={sync_ms}ms, fetch={fetch_ms}ms, build={build_ms}ms, total={total_ms}ms)" - ) - return Response( - { - "status": "success", - "code": "layer_published", - "message": ( - f"Successfully published {singular} layer " - f"'{layer_store_name}' to GeoServer with {record_count} record(s)." - ), - "data": { - "request_id": request_id, - "layer_type": kind, - "workspace": kind, - "layer_store_name": layer_store_name, - "record_count": record_count, - **context, - "sync_status": "success" if sync_ok else "failed", - "sync_duration_ms": sync_ms, - "fetch_duration_ms": fetch_ms, - "build_duration_ms": build_ms, - "total_duration_ms": total_ms, - }, - }, - status=status.HTTP_201_CREATED, - ) - - -# api's for add settlement, add well, add waterbody | add work [new, maintenance] -@api_view(["POST"]) -@auth_free -@schema(None) -def add_resources(request): - """ - Build and publish a GeoServer 'resources' layer for the given plan/block. - - Supported resource_type values: settlement, well, waterbody, cropping. - Layer naming convention: ___. - """ - return _build_layer_for_kind(request, kind="resources") - - -@api_view(["POST"]) -@auth_free -@schema(None) -def add_works(request): - """ - Build and publish a GeoServer 'works' layer for the given plan/block. - - Supported work_type values: - plan_gw — new recharge structures (groundwater) - main_gw — maintenance of recharge structures - plan_agri — new irrigation structures - main_agri — maintenance of irrigation structures - main_swb — surface water body maintenance - main_swb_rs — remote-sensed surface water body maintenance - livelihood — livelihood - agrohorticulture — agrohorticulture - - Layer naming convention: ___. - """ - return _build_layer_for_kind(request, kind="works") - - -# MARK: SYNC OFFLINE DATA HELPER FUNCTIONS -def _get_resource_config() -> Dict[str, Dict[str, Any]]: - """Configuration mapping for different resource types.""" - return { - "settlement": { - "url": ODK_SYNC_URL_SETTLEMENT, - "success_message": "Settlement data synced successfully", - }, - "well": { - "url": ODK_SYNC_URL_WELL, - "success_message": "Well data synced successfully", - }, - "water_structures": { - "url": ODK_SYNC_URL_WATER_STRUCTURES, - "success_message": "Water structures data synced successfully", - }, - "cropping_pattern": { - "url": ODK_SYNC_URL_CROP, - "success_message": "Cropping pattern data synced successfully", - }, - } - - -def _get_work_config() -> Dict[str, Dict[str, Any]]: - """Configuration mapping for different work types.""" - return { - "recharge_st": { - "url": ODK_SYNC_URL_RECHARGE_STRUCTURE, - "success_message": "Recharge structure data synced successfully", - }, - "irrigation_st": { - "url": ODK_SYNC_URL_IRRIGATION_STRUCTURE, - "success_message": "Irrigation structure data synced successfully", - }, - "propose_maintenance_recharge_st": { - "url": ODK_SYNC_URL_GW_MAINTENANCE, - "success_message": "Recharge structure maintenance data synced successfully", - }, - "propose_maintenance_rs_swb": { - "url": ODK_SYNC_URL_RS_WATERBODY_MAINTENANCE, - "success_message": "Surface water body maintenance data synced successfully", - }, - "propose_maintenance_ws_swb": { - "url": ODK_SYNC_URL_WATER_STRUCTURES_MAINTENANCE, - "success_message": "Water structures maintenance data synced successfully", - }, - "propose_maintenance_irrigation_st": { - "url": ODK_SYNC_URL_AGRI_MAINTENANCE, - "success_message": "Irrigation structure maintenance data synced successfully", - }, - "livelihood": { - "url": ODK_SYNC_URL_LIVELIHOOD, - "success_message": "Livelihood data synced successfully", - }, - "agrohorticulture": { - "url": ODK_SYNC_URL_AGROHORTICULTURE, - "success_message": "Agrohorticulture data synced successfully", - }, - } - - -def _get_feedback_config() -> Dict[str, Dict[str, Any]]: - """Configuration mapping of different feedback types""" - return { - "gw_feedback": { - "url": ODK_SYNC_URL_GW_FEEDBACK, - "success_message": "Groundwater feedback data synced successfully", - }, - "swb_feedback": { - "url": ODK_SYNC_URL_SWB_FEEDBACK, - "success_message": "Surface water body feedback data synced successfully", - }, - "agri_feedback": { - "url": ODK_SYNC_URL_AGRI_FEEDBACK, - "success_message": "Agriculture feedback data synced successfully", - }, - } - - -def _validate_sync_request( - request, resource_type: str = None, work_type: str = None, feedback_type: str = None -) -> Optional[Response]: - """Validate the sync request parameters and content type.""" - - if not resource_type and not work_type and not feedback_type: - return Response( - { - "error": "Must specify either resource_type or work_type or feedback_type" - }, - status=status.HTTP_400_BAD_REQUEST, - ) - - if resource_type: - valid_resources = ["settlement", "well", "water_structures", "cropping_pattern"] - if resource_type not in valid_resources: - return Response( - {"error": f"Invalid resource type. Must be one of {valid_resources}"}, - status=status.HTTP_400_BAD_REQUEST, - ) - - if work_type: - valid_work_types = [ - "recharge_st", - "irrigation_st", - "propose_maintenance_recharge_st", - "propose_maintenance_rs_swb", - "propose_maintenance_ws_swb", - "propose_maintenance_irrigation_st", - "livelihood", - "agrohorticulture", - ] - if work_type not in valid_work_types: - return Response( - {"error": f"Invalid work type. Must be one of {valid_work_types}"}, - status=status.HTTP_400_BAD_REQUEST, - ) - - if feedback_type: - valid_feedback_types = ["gw_feedback", "swb_feedback", "agri_feedback"] - if feedback_type not in valid_feedback_types: - return Response( - { - "error": f"Invalid feedback type. Must be one of {valid_feedback_types}" - }, - status=status.HTTP_400_BAD_REQUEST, - ) - - if request.content_type != "application/xml": - return Response( - {"error": "Content-Type must be application/xml"}, - status=status.HTTP_415_UNSUPPORTED_MEDIA_TYPE, - ) - - return None - - -def _sync_to_odk( - xml_string: str, - config: Dict[str, Any], - bearer_token: str, - category: str, - sync_type: str, -) -> Response: - """Handle the actual sync to ODK for a specific resource or work type.""" - sync_log = ODKSyncLog.objects.create( - category=category, - sync_type=sync_type, - xml_content=xml_string, - odk_url=config["url"], - status=ODKSyncLog.SyncStatus.PENDING, - ) - - try: - response = requests.post( - config["url"], - headers={ - "Content-Type": "application/xml", - "Authorization": f"Bearer {bearer_token}", - }, - data=xml_string, - ) - response.raise_for_status() - - odk_response = response.json() if response.content else None - sync_log.status = ODKSyncLog.SyncStatus.SUCCESS - sync_log.odk_response = odk_response - sync_log.save(update_fields=["status", "odk_response"]) - - return Response( - { - "sync_status": True, - "message": config["success_message"], - "odk_response": odk_response, - }, - status=status.HTTP_201_CREATED, - ) - - except requests.exceptions.RequestException as e: - item_name = config["success_message"].split()[0].lower() - print(f"Error syncing {item_name} data to ODK: {str(e)}") - - sync_log.status = ODKSyncLog.SyncStatus.FAILED - sync_log.error_details = str(e) - sync_log.save(update_fields=["status", "error_details"]) - - return Response( - { - "sync_status": False, - "error": f"Failed to sync {item_name} data to ODK", - "details": str(e), - }, - status=status.HTTP_500_INTERNAL_SERVER_ERROR, - ) - - -# MARK: SYNC OFFLINE DATA -# API to sync offline data coming from CC app -@api_view(["POST"]) -@csrf_exempt -@auth_free -@schema(None) -def sync_offline_data(request, resource_type=None, work_type=None, feedback_type=None): - """ - Sync data to ODK based on resource type or work type - Resource types: settlement, well, water_structures, cropping_pattern - Work types: "recharge_st", "irrigation_st", "propose_maintenance_recharge_st", "propose_maintenance_rs_swb", - "propose_maintenance_ws_swb", "propose_maintenance_irrigation_st", "livelihood", - Feedback types: "gw_feedback", "swb_feedback", "agri_feedback" - - fetch Bearer Token from ODK - - send xmlString to ODK - """ - print( - f"Inside sync_offline_data API for resource type: {resource_type}, work type: {work_type}, feedback type: {feedback_type}" - ) - - # Validate request - validation_error = _validate_sync_request( - request, resource_type, work_type, feedback_type - ) - if validation_error: - return validation_error - - if resource_type: - configs = _get_resource_config() - config = configs[resource_type] - category = ODKSyncLog.SyncCategory.RESOURCE - item_type = resource_type - elif work_type: - configs = _get_work_config() - config = configs[work_type] - category = ODKSyncLog.SyncCategory.WORK - item_type = work_type - elif feedback_type: - configs = _get_feedback_config() - config = configs[feedback_type] - category = ODKSyncLog.SyncCategory.FEEDBACK - item_type = feedback_type - else: - return Response( - {"error": "Invalid request"}, status=status.HTTP_400_BAD_REQUEST - ) - - xml_string = request.body.decode("utf-8") - print(f"Sync Category: {category}, Type: {item_type}") - - try: - bearer_token = fetch_bearer_token(ODK_USER_EMAIL_SYNC, ODK_USER_PASSWORD_SYNC) - print("Bearer Token: ", bearer_token) - - return _sync_to_odk(xml_string, config, bearer_token, category, item_type) - - except Exception as e: - print("Exception in sync_offline_data api :: ", e) - return Response({"error": str(e)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR) - - -# map plan to gp api -@api_view(["PATCh"]) -@schema(None) -def map_plan_to_gp(request): - - plan_id = request.data.get("plan_id") - gp_id = request.data.get("gp_id") - - if not plan_id or not gp_id: - return Response( - { - "success": False, - "message": "plan_id and gp_id are required", - }, - status=400, - ) - - try: - plan = PlanApp.objects.get(id=plan_id) - - except PlanApp.DoesNotExist: - return Response( - { - "success": False, - "message": "Plan not found", - }, - status=404, - ) - - try: - gp = GramPanchayat.objects.get(gram_panchayat_code=gp_id) - - except GramPanchayat.DoesNotExist: - return Response( - { - "success": False, - "message": "Gram Panchayat not found", - }, - status=404, - ) - - # GP should belong to same tehsil - - if plan.tehsil_soi_id != gp.tehsil_id: - return Response( - { - "success": False, - "message": "Selected GP does not belong to plan tehsil", - }, - status=400, - ) - - plan.gp = gp - plan.updated_by = request.user - - plan.save(update_fields=["gp", "updated_by", "updated_at"]) - - return Response( - { - "success": True, - "message": "Plan mapped with GP successfully", - "data": { - "plan_id": plan.id, - "gp_id": gp.gram_panchayat_code, - "gp_name": gp.gram_panchayat_name, - }, - } - ) +import logging +import os +import time +import uuid +from typing import Any, Dict, List, Optional, Tuple + +import requests +from django.views.decorators.csrf import csrf_exempt +from rest_framework import status +from rest_framework.decorators import api_view, schema +from rest_framework.response import Response + +from dpr.utils import transform_name +from moderation.utils.update_csdb import sync_form_type +from nrm_app.settings import ODK_USER_EMAIL_SYNC, ODK_USER_PASSWORD_SYNC, TMP_LOCATION +from utilities.auth_check_decorator import api_security_check +from utilities.auth_utils import auth_free +from utilities.constants import ( + ODK_SYNC_URL_AGRI_FEEDBACK, + ODK_SYNC_URL_AGRI_MAINTENANCE, + ODK_SYNC_URL_AGROHORTICULTURE, + ODK_SYNC_URL_CROP, + ODK_SYNC_URL_GW_FEEDBACK, + ODK_SYNC_URL_GW_MAINTENANCE, + ODK_SYNC_URL_IRRIGATION_STRUCTURE, + ODK_SYNC_URL_LIVELIHOOD, + ODK_SYNC_URL_RECHARGE_STRUCTURE, + ODK_SYNC_URL_RS_WATERBODY_MAINTENANCE, + ODK_SYNC_URL_SETTLEMENT, + ODK_SYNC_URL_SWB_FEEDBACK, + ODK_SYNC_URL_WATER_STRUCTURES, + ODK_SYNC_URL_WATER_STRUCTURES_MAINTENANCE, + ODK_SYNC_URL_WELL, +) + +logger = logging.getLogger(__name__) + +from .build_layer import build_layer +from .models import ODKSyncLog, PlanApp, Plan +from .serializers import PlanAppSerializer +from .utils import fetch_bearer_token, fetch_db_data +from geoadmin.models import GramPanchayat +from django.db.models import Q + +_COMMON_REQUIRED_FIELDS: Tuple[str, ...] = ( + "layer_name", + "plan_id", + "plan_name", + "district_name", + "block_name", +) + +_LAYER_KIND_CONFIG: Dict[str, Dict[str, str]] = { + "resources": {"type_field": "resource_type", "singular": "resource"}, + "works": {"type_field": "work_type", "singular": "work"}, +} + + +# MARK: Get Plans API +@api_security_check(auth_type="Auth_free") +@schema(None) +def get_plans(request): + """ + Get Plans API + + Args: + block_id (str, optional): Block ID. Defaults to None. + + Returns: + Response: JSON response containing a list of plans of a block or all the plans + """ + try: + block_id = request.query_params.get("block_id", None) + if block_id is not None: + plans = Plan.objects.filter(block=block_id) + else: + plans = Plan.objects.all() + serializer = PlanAppSerializer(plans, many=True) + response = {"plans": serializer.data} + + return Response(response, status=status.HTTP_200_OK) + except Exception as e: + print("Exception in get_plans api :: ", e) + return Response({"Exception": e}, status=status.HTTP_500_INTERNAL_SERVER_ERROR) + + +@api_view(["POST"]) +@auth_free +@schema(None) +def add_plan(request): + if request.method == "POST": + serializer = PlanAppSerializer(data=request.data) + if serializer.is_valid(): + serializer.save() # Save the new Plan instance if validation passes + return Response(serializer.data, status=status.HTTP_201_CREATED) + else: + return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) + return Response( + {"error": "Method not allowed"}, status=status.HTTP_405_METHOD_NOT_ALLOWED + ) + + +# MARK: Build Layer Helpers (shared by /add_resources and /add_works) +def _extract_payload(request, kind: str) -> Tuple[Optional[Dict[str, Any]], List[str]]: + """Pull and normalize the request payload. Returns (payload, missing_fields).""" + type_field = _LAYER_KIND_CONFIG[kind]["type_field"] + required = (*_COMMON_REQUIRED_FIELDS, type_field) + + missing = [f for f in required if not request.data.get(f)] + if missing: + return None, missing + + def _lower(value: Any) -> Any: + return value.lower() if isinstance(value, str) else value + + return { + "layer_name": _lower(request.data.get("layer_name")), + "item_type": _lower(request.data.get(type_field)), + "plan_id": request.data.get("plan_id"), + "plan_name": _lower(request.data.get("plan_name")), + "district": _lower(request.data.get("district_name")), + "block": _lower(request.data.get("block_name")), + }, [] + + +def _expected_layer_store_name( + item_type: str, plan_id: Any, district: str, block: str +) -> str: + """Mirror the naming convention used by build_layer.build_layer for transparency.""" + return f"{item_type}_{plan_id}_{district}_{transform_name(name=block)}" + + +def _safe_unlink(csv_path: str, request_id: str, kind: str) -> None: + try: + if os.path.exists(csv_path): + os.remove(csv_path) + logger.info( + f"[{request_id}] {kind}.build: cleaned up temp CSV at {csv_path}" + ) + except OSError as exc: + logger.warning( + f"[{request_id}] {kind}.build: failed to remove temp CSV at " + f"{csv_path}: {exc}" + ) + + +def _error_response( + request_id: str, + code: str, + message: str, + http_status: int, + extra: Optional[Dict[str, Any]] = None, +) -> Response: + data: Dict[str, Any] = {"request_id": request_id} + if extra: + data.update(extra) + return Response( + { + "status": "error", + "code": code, + "error": message, + "data": data, + }, + status=http_status, + ) + + +def _build_layer_for_kind(request, kind: str) -> Response: + """ + Shared workflow for /add_resources and /add_works: + 1. validate payload + 2. trigger incremental ODK -> DB sync (best-effort) + 3. fetch source records from DB and stage a CSV + 4. publish the layer to GeoServer + 5. clean up the temp CSV and return a structured response + """ + request_id = uuid.uuid4().hex[:12] + type_field = _LAYER_KIND_CONFIG[kind]["type_field"] + singular = _LAYER_KIND_CONFIG[kind]["singular"] + started_at = time.perf_counter() + + logger.info( + f"[{request_id}] {kind}.build: request received " + f"(content_type={request.content_type}, keys={list(request.data.keys())})" + ) + + payload, missing = _extract_payload(request, kind) + if missing: + logger.warning( + f"[{request_id}] {kind}.build: rejecting request — " + f"missing/empty fields: {missing}" + ) + return _error_response( + request_id, + code="missing_fields", + message=f"Missing required field(s): {', '.join(missing)}.", + http_status=status.HTTP_400_BAD_REQUEST, + extra={"missing_fields": missing}, + ) + + item_type = payload["item_type"] + plan_id = payload["plan_id"] + plan_name = payload["plan_name"] + district = payload["district"] + block = payload["block"] + layer_name = payload["layer_name"] + + context = { + type_field: item_type, + "plan_id": plan_id, + "plan_name": plan_name, + "district": district, + "block": block, + "layer_name": layer_name, + } + logger.info(f"[{request_id}] {kind}.build: payload normalized — {context}") + + csv_path = os.path.join(TMP_LOCATION, f"{item_type}_{plan_id}_{block}.csv") + logger.info(f"[{request_id}] {kind}.build: temp CSV path resolved to {csv_path}") + + sync_started = time.perf_counter() + logger.info( + f"[{request_id}] {kind}.build: triggering incremental ODK sync for " + f"{type_field}={item_type}" + ) + sync_ok = sync_form_type(item_type) + sync_ms = int((time.perf_counter() - sync_started) * 1000) + if sync_ok: + logger.info( + f"[{request_id}] {kind}.build: ODK sync completed for " + f"{type_field}={item_type} in {sync_ms}ms" + ) + else: + logger.warning( + f"[{request_id}] {kind}.build: ODK sync FAILED for " + f"{type_field}={item_type} in {sync_ms}ms; proceeding with existing DB data" + ) + + fetch_started = time.perf_counter() + logger.info( + f"[{request_id}] {kind}.build: fetching DB data for {type_field}={item_type}, " + f"plan_id={plan_id}, block={block}" + ) + try: + record_count = fetch_db_data(csv_path, item_type, block, plan_id) + except Exception as exc: + fetch_ms = int((time.perf_counter() - fetch_started) * 1000) + logger.exception( + f"[{request_id}] {kind}.build: unexpected error during fetch_db_data " + f"for {type_field}={item_type}, plan_id={plan_id} " + f"(fetch_ms={fetch_ms}): {exc}" + ) + _safe_unlink(csv_path, request_id, kind) + return _error_response( + request_id, + code="db_fetch_failed", + message="Failed to fetch source data from the database.", + http_status=status.HTTP_500_INTERNAL_SERVER_ERROR, + extra={ + **context, + "details": str(exc), + "sync_status": "success" if sync_ok else "failed", + "sync_duration_ms": sync_ms, + "fetch_duration_ms": fetch_ms, + }, + ) + fetch_ms = int((time.perf_counter() - fetch_started) * 1000) + + if not record_count: + total_ms = int((time.perf_counter() - started_at) * 1000) + logger.warning( + f"[{request_id}] {kind}.build: no DB data found for " + f"{type_field}={item_type}, plan_id={plan_id}, block={block} " + f"(sync_ok={sync_ok}, fetch_ms={fetch_ms}, total_ms={total_ms})" + ) + return _error_response( + request_id, + code="no_data_found", + message=( + f"No records found for {type_field}='{item_type}', " + f"plan_id='{plan_id}', block='{block}'." + ), + http_status=status.HTTP_404_NOT_FOUND, + extra={ + **context, + "record_count": 0, + "sync_status": "success" if sync_ok else "failed", + "sync_duration_ms": sync_ms, + "fetch_duration_ms": fetch_ms, + "total_duration_ms": total_ms, + }, + ) + logger.info( + f"[{request_id}] {kind}.build: DB fetch staged {record_count} row(s) " + f"in {fetch_ms}ms" + ) + + layer_store_name = _expected_layer_store_name(item_type, plan_id, district, block) + build_started = time.perf_counter() + logger.info( + f"[{request_id}] {kind}.build: publishing GeoServer layer " + f"workspace='{kind}', store='{layer_store_name}'" + ) + try: + success = build_layer( + layer_type=kind, + item_type=item_type, + plan_id=plan_id, + district=district, + block=block, + csv_path=csv_path, + ) + except Exception as exc: + build_ms = int((time.perf_counter() - build_started) * 1000) + total_ms = int((time.perf_counter() - started_at) * 1000) + logger.exception( + f"[{request_id}] {kind}.build: unexpected error during build_layer for " + f"{type_field}={item_type}, plan_id={plan_id} " + f"(build_ms={build_ms}, total_ms={total_ms}): {exc}" + ) + _safe_unlink(csv_path, request_id, kind) + return _error_response( + request_id, + code="internal_error", + message="An unexpected error occurred while building the layer.", + http_status=status.HTTP_500_INTERNAL_SERVER_ERROR, + extra={ + **context, + "layer_store_name": layer_store_name, + "details": str(exc), + "sync_status": "success" if sync_ok else "failed", + "sync_duration_ms": sync_ms, + "fetch_duration_ms": fetch_ms, + "build_duration_ms": build_ms, + "total_duration_ms": total_ms, + }, + ) + finally: + _safe_unlink(csv_path, request_id, kind) + + build_ms = int((time.perf_counter() - build_started) * 1000) + total_ms = int((time.perf_counter() - started_at) * 1000) + + if not success: + logger.error( + f"[{request_id}] {kind}.build: build_layer returned False for " + f"{type_field}={item_type}, plan_id={plan_id} " + f"(build_ms={build_ms}, total_ms={total_ms})" + ) + return _error_response( + request_id, + code="layer_build_failed", + message=( + f"Failed to publish GeoServer layer '{layer_store_name}'. " + "See server logs for details." + ), + http_status=status.HTTP_500_INTERNAL_SERVER_ERROR, + extra={ + **context, + "layer_store_name": layer_store_name, + "record_count": record_count, + "sync_status": "success" if sync_ok else "failed", + "sync_duration_ms": sync_ms, + "fetch_duration_ms": fetch_ms, + "build_duration_ms": build_ms, + "total_duration_ms": total_ms, + }, + ) + + logger.info( + f"[{request_id}] {kind}.build: SUCCESS — published layer " + f"'{layer_store_name}' ({record_count} row(s)) in workspace='{kind}' " + f"(sync={sync_ms}ms, fetch={fetch_ms}ms, build={build_ms}ms, total={total_ms}ms)" + ) + return Response( + { + "status": "success", + "code": "layer_published", + "message": ( + f"Successfully published {singular} layer " + f"'{layer_store_name}' to GeoServer with {record_count} record(s)." + ), + "data": { + "request_id": request_id, + "layer_type": kind, + "workspace": kind, + "layer_store_name": layer_store_name, + "record_count": record_count, + **context, + "sync_status": "success" if sync_ok else "failed", + "sync_duration_ms": sync_ms, + "fetch_duration_ms": fetch_ms, + "build_duration_ms": build_ms, + "total_duration_ms": total_ms, + }, + }, + status=status.HTTP_201_CREATED, + ) + + +# api's for add settlement, add well, add waterbody | add work [new, maintenance] +@api_view(["POST"]) +@auth_free +@schema(None) +def add_resources(request): + """ + Build and publish a GeoServer 'resources' layer for the given plan/block. + + Supported resource_type values: settlement, well, waterbody, cropping. + Layer naming convention: ___. + """ + return _build_layer_for_kind(request, kind="resources") + + +@api_view(["POST"]) +@auth_free +@schema(None) +def add_works(request): + """ + Build and publish a GeoServer 'works' layer for the given plan/block. + + Supported work_type values: + plan_gw — new recharge structures (groundwater) + main_gw — maintenance of recharge structures + plan_agri — new irrigation structures + main_agri — maintenance of irrigation structures + main_swb — surface water body maintenance + main_swb_rs — remote-sensed surface water body maintenance + livelihood — livelihood + agrohorticulture — agrohorticulture + + Layer naming convention: ___. + """ + return _build_layer_for_kind(request, kind="works") + + +# MARK: SYNC OFFLINE DATA HELPER FUNCTIONS +def _get_resource_config() -> Dict[str, Dict[str, Any]]: + """Configuration mapping for different resource types.""" + return { + "settlement": { + "url": ODK_SYNC_URL_SETTLEMENT, + "success_message": "Settlement data synced successfully", + }, + "well": { + "url": ODK_SYNC_URL_WELL, + "success_message": "Well data synced successfully", + }, + "water_structures": { + "url": ODK_SYNC_URL_WATER_STRUCTURES, + "success_message": "Water structures data synced successfully", + }, + "cropping_pattern": { + "url": ODK_SYNC_URL_CROP, + "success_message": "Cropping pattern data synced successfully", + }, + } + + +def _get_work_config() -> Dict[str, Dict[str, Any]]: + """Configuration mapping for different work types.""" + return { + "recharge_st": { + "url": ODK_SYNC_URL_RECHARGE_STRUCTURE, + "success_message": "Recharge structure data synced successfully", + }, + "irrigation_st": { + "url": ODK_SYNC_URL_IRRIGATION_STRUCTURE, + "success_message": "Irrigation structure data synced successfully", + }, + "propose_maintenance_recharge_st": { + "url": ODK_SYNC_URL_GW_MAINTENANCE, + "success_message": "Recharge structure maintenance data synced successfully", + }, + "propose_maintenance_rs_swb": { + "url": ODK_SYNC_URL_RS_WATERBODY_MAINTENANCE, + "success_message": "Surface water body maintenance data synced successfully", + }, + "propose_maintenance_ws_swb": { + "url": ODK_SYNC_URL_WATER_STRUCTURES_MAINTENANCE, + "success_message": "Water structures maintenance data synced successfully", + }, + "propose_maintenance_irrigation_st": { + "url": ODK_SYNC_URL_AGRI_MAINTENANCE, + "success_message": "Irrigation structure maintenance data synced successfully", + }, + "livelihood": { + "url": ODK_SYNC_URL_LIVELIHOOD, + "success_message": "Livelihood data synced successfully", + }, + "agrohorticulture": { + "url": ODK_SYNC_URL_AGROHORTICULTURE, + "success_message": "Agrohorticulture data synced successfully", + }, + } + + +def _get_feedback_config() -> Dict[str, Dict[str, Any]]: + """Configuration mapping of different feedback types""" + return { + "gw_feedback": { + "url": ODK_SYNC_URL_GW_FEEDBACK, + "success_message": "Groundwater feedback data synced successfully", + }, + "swb_feedback": { + "url": ODK_SYNC_URL_SWB_FEEDBACK, + "success_message": "Surface water body feedback data synced successfully", + }, + "agri_feedback": { + "url": ODK_SYNC_URL_AGRI_FEEDBACK, + "success_message": "Agriculture feedback data synced successfully", + }, + } + + +def _validate_sync_request( + request, resource_type: str = None, work_type: str = None, feedback_type: str = None +) -> Optional[Response]: + """Validate the sync request parameters and content type.""" + + if not resource_type and not work_type and not feedback_type: + return Response( + { + "error": "Must specify either resource_type or work_type or feedback_type" + }, + status=status.HTTP_400_BAD_REQUEST, + ) + + if resource_type: + valid_resources = ["settlement", "well", "water_structures", "cropping_pattern"] + if resource_type not in valid_resources: + return Response( + {"error": f"Invalid resource type. Must be one of {valid_resources}"}, + status=status.HTTP_400_BAD_REQUEST, + ) + + if work_type: + valid_work_types = [ + "recharge_st", + "irrigation_st", + "propose_maintenance_recharge_st", + "propose_maintenance_rs_swb", + "propose_maintenance_ws_swb", + "propose_maintenance_irrigation_st", + "livelihood", + "agrohorticulture", + ] + if work_type not in valid_work_types: + return Response( + {"error": f"Invalid work type. Must be one of {valid_work_types}"}, + status=status.HTTP_400_BAD_REQUEST, + ) + + if feedback_type: + valid_feedback_types = ["gw_feedback", "swb_feedback", "agri_feedback"] + if feedback_type not in valid_feedback_types: + return Response( + { + "error": f"Invalid feedback type. Must be one of {valid_feedback_types}" + }, + status=status.HTTP_400_BAD_REQUEST, + ) + + if request.content_type != "application/xml": + return Response( + {"error": "Content-Type must be application/xml"}, + status=status.HTTP_415_UNSUPPORTED_MEDIA_TYPE, + ) + + return None + + +def _sync_to_odk( + xml_string: str, + config: Dict[str, Any], + bearer_token: str, + category: str, + sync_type: str, +) -> Response: + """Handle the actual sync to ODK for a specific resource or work type.""" + sync_log = ODKSyncLog.objects.create( + category=category, + sync_type=sync_type, + xml_content=xml_string, + odk_url=config["url"], + status=ODKSyncLog.SyncStatus.PENDING, + ) + + try: + response = requests.post( + config["url"], + headers={ + "Content-Type": "application/xml", + "Authorization": f"Bearer {bearer_token}", + }, + data=xml_string, + ) + response.raise_for_status() + + odk_response = response.json() if response.content else None + sync_log.status = ODKSyncLog.SyncStatus.SUCCESS + sync_log.odk_response = odk_response + sync_log.save(update_fields=["status", "odk_response"]) + + return Response( + { + "sync_status": True, + "message": config["success_message"], + "odk_response": odk_response, + }, + status=status.HTTP_201_CREATED, + ) + + except requests.exceptions.RequestException as e: + item_name = config["success_message"].split()[0].lower() + print(f"Error syncing {item_name} data to ODK: {str(e)}") + + sync_log.status = ODKSyncLog.SyncStatus.FAILED + sync_log.error_details = str(e) + sync_log.save(update_fields=["status", "error_details"]) + + return Response( + { + "sync_status": False, + "error": f"Failed to sync {item_name} data to ODK", + "details": str(e), + }, + status=status.HTTP_500_INTERNAL_SERVER_ERROR, + ) + + +# MARK: SYNC OFFLINE DATA +# API to sync offline data coming from CC app +@api_view(["POST"]) +@csrf_exempt +@auth_free +@schema(None) +def sync_offline_data(request, resource_type=None, work_type=None, feedback_type=None): + """ + Sync data to ODK based on resource type or work type + Resource types: settlement, well, water_structures, cropping_pattern + Work types: "recharge_st", "irrigation_st", "propose_maintenance_recharge_st", "propose_maintenance_rs_swb", + "propose_maintenance_ws_swb", "propose_maintenance_irrigation_st", "livelihood", + Feedback types: "gw_feedback", "swb_feedback", "agri_feedback" + - fetch Bearer Token from ODK + - send xmlString to ODK + """ + print( + f"Inside sync_offline_data API for resource type: {resource_type}, work type: {work_type}, feedback type: {feedback_type}" + ) + + # Validate request + validation_error = _validate_sync_request( + request, resource_type, work_type, feedback_type + ) + if validation_error: + return validation_error + + if resource_type: + configs = _get_resource_config() + config = configs[resource_type] + category = ODKSyncLog.SyncCategory.RESOURCE + item_type = resource_type + elif work_type: + configs = _get_work_config() + config = configs[work_type] + category = ODKSyncLog.SyncCategory.WORK + item_type = work_type + elif feedback_type: + configs = _get_feedback_config() + config = configs[feedback_type] + category = ODKSyncLog.SyncCategory.FEEDBACK + item_type = feedback_type + else: + return Response( + {"error": "Invalid request"}, status=status.HTTP_400_BAD_REQUEST + ) + + xml_string = request.body.decode("utf-8") + print(f"Sync Category: {category}, Type: {item_type}") + + try: + bearer_token = fetch_bearer_token(ODK_USER_EMAIL_SYNC, ODK_USER_PASSWORD_SYNC) + print("Bearer Token: ", bearer_token) + + return _sync_to_odk(xml_string, config, bearer_token, category, item_type) + + except Exception as e: + print("Exception in sync_offline_data api :: ", e) + return Response({"error": str(e)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR) + + +# map plan to gp api +@api_view(["PATCh"]) +@schema(None) +def map_plan_to_gp(request): + + plan_id = request.data.get("plan_id") + gp_id = request.data.get("gp_id") + + if not plan_id or not gp_id: + return Response( + { + "success": False, + "message": "plan_id and gp_id are required", + }, + status=400, + ) + + try: + plan = PlanApp.objects.get(id=plan_id) + + except PlanApp.DoesNotExist: + return Response( + { + "success": False, + "message": "Plan not found", + }, + status=404, + ) + + try: + gp = GramPanchayat.objects.get(gram_panchayat_code=gp_id) + + except GramPanchayat.DoesNotExist: + return Response( + { + "success": False, + "message": "Gram Panchayat not found", + }, + status=404, + ) + + # GP should belong to same tehsil + + if plan.tehsil_soi_id != gp.tehsil_id: + return Response( + { + "success": False, + "message": "Selected GP does not belong to plan tehsil", + }, + status=400, + ) + + plan.gp = gp + plan.updated_by = request.user + + plan.save(update_fields=["gp", "updated_by", "updated_at"]) + + return Response( + { + "success": True, + "message": "Plan mapped with GP successfully", + "data": { + "plan_id": plan.id, + "gp_id": gp.gram_panchayat_code, + "gp_name": gp.gram_panchayat_name, + }, + } + ) + + +@api_view(["GET"]) +@schema(None) +def plan_count(request): + """ + gives plan count on the basis of org_id or project_id and filter + """ + org_id = request.query_params.get("org_id") + project_id = request.query_params.get("project_id") + is_completed = request.query_params.get("is_completed") + + queryset = PlanApp.objects.filter(enabled=True).exclude( + Q(plan__icontains="test") | Q(plan__icontains="demo") + ) + + if is_completed: + queryset = queryset.filter(is_completed=True).exclude( + Q(plan__icontains="test") | Q(plan__icontains="demo") + ) + + if org_id: + plan_count = queryset.filter(organization=org_id).count() + elif project_id: + plan_count = queryset.filter(project=project_id).count() + else: + plan_count = 0 + + return Response({"plan_count": plan_count}) diff --git a/plans/tests.py b/plans/tests.py index bb68993b..71fe76ea 100755 --- a/plans/tests.py +++ b/plans/tests.py @@ -1,556 +1,556 @@ -# plans/tests.py -import csv -import os -import tempfile -from datetime import datetime, timezone -from unittest.mock import patch - -from django.test import TestCase -from django.urls import reverse -from rest_framework.test import APITestCase, APIClient -from rest_framework import status - -from dpr.models import ODK_settlement -from .models import Plan -from .utils import fetch_db_data -from projects.models import Project, AppType -from organization.models import Organization -from users.models import User, UserProjectGroup -from django.contrib.auth.models import Group, Permission - - -class PlanModelTest(TestCase): - def setUp(self): - # Create organization - self.organization = Organization.objects.create(name="Test Organization") - - # Create project with app_type - self.project = Project.objects.create( - name="Test Project", - organization=self.organization, - app_type=AppType.WATERSHED, - enabled=True, - ) - - # Create user - self.user = User.objects.create_user( - username="testuser", - email="test@example.com", - password="password123", - organization=self.organization, - ) - - def test_plan_creation(self): - plan = Plan.objects.create( - name="Test Watershed Plan", - project=self.project, - organization=self.organization, - state="Test State", - district="Test District", - block="Test Block", - village="Test Village", - gram_panchayat="Test GP", - created_by=self.user, - ) - - # Check model attributes - self.assertEqual(plan.name, "Test Watershed Plan") - self.assertEqual(plan.project, self.project) - self.assertEqual(plan.organization, self.organization) - self.assertEqual(plan.state, "Test State") - self.assertEqual(plan.district, "Test District") - self.assertEqual(plan.created_by, self.user) - - -class PlanAPITest(APITestCase): - def setUp(self): - self.client = APIClient() - - # Create organization - self.organization = Organization.objects.create(name="Test Organization") - - # Create project with app_type - self.project = Project.objects.create( - name="Test Project", - organization=self.organization, - app_type=AppType.WATERSHED, - enabled=True, - ) - - # Create admin user - self.admin_user = User.objects.create_user( - username="admin", - email="admin@example.com", - password="password123", - organization=self.organization, - is_superadmin=True, - ) - - # Create edit user - self.edit_user = User.objects.create_user( - username="editor", - email="editor@example.com", - password="password123", - organization=self.organization, - ) - - # Create view user - self.view_user = User.objects.create_user( - username="viewer", - email="viewer@example.com", - password="password123", - organization=self.organization, - ) - - # Create groups and permissions - self.admin_group = Group.objects.create(name="Project Admin") - self.editor_group = Group.objects.create(name="Project Editor") - self.viewer_group = Group.objects.create(name="Project Viewer") - - # Create permissions - Permission.objects.get_or_create( - codename="view_watershed", - name="Can view watershed planning data", - content_type_id=1, # This would typically be correct content type ID - ) - - Permission.objects.get_or_create( - codename="add_watershed", - name="Can add watershed planning data", - content_type_id=1, - ) - - Permission.objects.get_or_create( - codename="change_watershed", - name="Can change watershed planning data", - content_type_id=1, - ) - - Permission.objects.get_or_create( - codename="delete_watershed", - name="Can delete watershed planning data", - content_type_id=1, - ) - - # Assign permissions to groups - view_perm = Permission.objects.get(codename="view_watershed") - add_perm = Permission.objects.get(codename="add_watershed") - change_perm = Permission.objects.get(codename="change_watershed") - delete_perm = Permission.objects.get(codename="delete_watershed") - - self.admin_group.permissions.add(view_perm, add_perm, change_perm, delete_perm) - self.editor_group.permissions.add(view_perm, add_perm, change_perm) - self.viewer_group.permissions.add(view_perm) - - # Assign users to project roles - UserProjectGroup.objects.create( - user=self.edit_user, project=self.project, group=self.editor_group - ) - - UserProjectGroup.objects.create( - user=self.view_user, project=self.project, group=self.viewer_group - ) - - # Create a test plan - self.plan = Plan.objects.create( - name="Test Watershed Plan", - project=self.project, - organization=self.organization, - state="Test State", - district="Test District", - block="Test Block", - village="Test Village", - gram_panchayat="Test GP", - created_by=self.admin_user, - ) - - # URLs - self.plans_list_url = reverse( - "project-plan-list", kwargs={"project_pk": self.project.pk} - ) - self.plan_detail_url = reverse( - "project-plan-detail", - kwargs={"project_pk": self.project.pk, "pk": self.plan.pk}, - ) - - def test_list_plans_as_admin(self): - self.client.force_authenticate(user=self.admin_user) - response = self.client.get(self.plans_list_url) - - self.assertEqual(response.status_code, status.HTTP_200_OK) - self.assertEqual(len(response.data), 1) - - def test_list_plans_as_editor(self): - self.client.force_authenticate(user=self.edit_user) - response = self.client.get(self.plans_list_url) - - self.assertEqual(response.status_code, status.HTTP_200_OK) - self.assertEqual(len(response.data), 1) - - def test_list_plans_as_viewer(self): - self.client.force_authenticate(user=self.view_user) - response = self.client.get(self.plans_list_url) - - self.assertEqual(response.status_code, status.HTTP_200_OK) - self.assertEqual(len(response.data), 1) - - def test_create_plan_as_admin(self): - self.client.force_authenticate(user=self.admin_user) - data = { - "name": "New Watershed Plan", - "state": "New State", - "district": "New District", - "block": "New Block", - "village": "New Village", - "gram_panchayat": "New GP", - } - - response = self.client.post(self.plans_list_url, data) - - self.assertEqual(response.status_code, status.HTTP_201_CREATED) - self.assertEqual(response.data["name"], "New Watershed Plan") - self.assertEqual(response.data["state"], "New State") - - # Check plan was created in database - self.assertEqual(Plan.objects.count(), 2) - - def test_create_plan_as_editor(self): - self.client.force_authenticate(user=self.edit_user) - data = { - "name": "Editor Plan", - "state": "Editor State", - "district": "Editor District", - "block": "Editor Block", - "village": "Editor Village", - "gram_panchayat": "Editor GP", - } - - response = self.client.post(self.plans_list_url, data) - - # Editor should be able to create plans - self.assertEqual(response.status_code, status.HTTP_201_CREATED) - self.assertEqual(response.data["name"], "Editor Plan") - - def test_create_plan_as_viewer(self): - self.client.force_authenticate(user=self.view_user) - data = { - "name": "Viewer Plan", - "state": "Viewer State", - "district": "Viewer District", - "block": "Viewer Block", - "village": "Viewer Village", - "gram_panchayat": "Viewer GP", - } - - response = self.client.post(self.plans_list_url, data) - - # Viewer should not be able to create plans - self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) - - def test_update_plan_as_admin(self): - self.client.force_authenticate(user=self.admin_user) - data = { - "name": "Updated Plan", - "state": "Updated State", - "district": "Updated District", - "block": "Updated Block", - "village": "Updated Village", - "gram_panchayat": "Updated GP", - } - - response = self.client.put(self.plan_detail_url, data) - - self.assertEqual(response.status_code, status.HTTP_200_OK) - self.assertEqual(response.data["name"], "Updated Plan") - - # Verify database was updated - updated_plan = Plan.objects.get(pk=self.plan.pk) - self.assertEqual(updated_plan.name, "Updated Plan") - - def test_update_plan_as_editor(self): - self.client.force_authenticate(user=self.edit_user) - data = { - "name": "Editor Updated", - "state": self.plan.state, - "district": self.plan.district, - "block": self.plan.block, - "village": self.plan.village, - "gram_panchayat": self.plan.gram_panchayat, - } - - response = self.client.patch(self.plan_detail_url, {"name": "Editor Updated"}) - - # Editor should be able to update plans - self.assertEqual(response.status_code, status.HTTP_200_OK) - self.assertEqual(response.data["name"], "Editor Updated") - - def test_update_plan_as_viewer(self): - self.client.force_authenticate(user=self.view_user) - data = {"name": "Viewer Updated"} - - response = self.client.patch(self.plan_detail_url, data) - - # Viewer should not be able to update plans - self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) - - def test_delete_plan_as_admin(self): - self.client.force_authenticate(user=self.admin_user) - response = self.client.delete(self.plan_detail_url) - - self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT) - - # Verify plan was deleted - self.assertEqual(Plan.objects.count(), 0) - - def test_delete_plan_as_editor(self): - self.client.force_authenticate(user=self.edit_user) - response = self.client.delete(self.plan_detail_url) - - # Editor should not be able to delete plans - self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) - - # Verify plan was not deleted - self.assertEqual(Plan.objects.count(), 1) - - def test_delete_plan_as_viewer(self): - self.client.force_authenticate(user=self.view_user) - response = self.client.delete(self.plan_detail_url) - - # Viewer should not be able to delete plans - self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) - - # Verify plan was not deleted - self.assertEqual(Plan.objects.count(), 1) - - -# Minimal valid ODK settlement JSON (same structure as ODK submissions stored in data_settlement) -def _make_settlement_json(plan_id, block_name, settlement_id="SETT001", review_state="hasIssues"): - return { - "__id": f"uuid:{settlement_id}", - "__system": {"reviewState": review_state, "submissionDate": "2024-01-01T00:00:00Z"}, - "block_name": block_name, - "plan_id": str(plan_id), - "GPS_point": { - "point_mapsappearance": { - "coordinates": [78.5, 20.5] - } - }, - "Settlements_id": settlement_id, - "Settlements_name": "Test Settlement", - "MNREGA_INFORMATION": { - "NREGA_aware": 10, - "NREGA_applied": 5, - "NREGA_job_card": 3, - "total_household": 2, - "NREGA_work_days": 100, - "q1": "yes", - "select_one_Y_N": "yes", - "select_one_demands": "wages", - "select_multiple_issues": "delayed_payment", - "select_one_contributions": "labour", - }, - } - - -def _create_settlement(plan_id, block_name, settlement_id="SETT001", - is_deleted=False, is_moderated=False, review_state="hasIssues", - data_override=None): - data = data_override or _make_settlement_json(plan_id, block_name, settlement_id, review_state) - return ODK_settlement.objects.create( - settlement_id=settlement_id, - settlement_name="Test Settlement", - submission_time=datetime(2024, 1, 1, tzinfo=timezone.utc), - submitted_by="test_user", - status_re=review_state, - latitude=20.5, - longitude=78.5, - block_name=block_name, - number_of_households=10, - largest_caste="General", - smallest_caste="SC", - settlement_status="active", - plan_id=str(plan_id), - plan_name="Test Plan", - uuid=f"uuid:{settlement_id}", - farmer_family={}, - livestock_census={}, - nrega_job_aware=10, - nrega_job_applied=5, - nrega_past_work="yes", - nrega_raise_demand="yes", - nrega_demand="wages", - nrega_issues="delayed_payment", - nrega_community="labour", - data_settlement=data, - is_deleted=is_deleted, - is_moderated=is_moderated, - ) - - -class FetchDbDataTest(TestCase): - - def setUp(self): - self.tmp_dir = tempfile.mkdtemp() - - def _csv_path(self, name="test.csv"): - return os.path.join(self.tmp_dir, name) - - def test_returns_true_and_writes_csv_for_valid_settlement(self): - _create_settlement(plan_id="42", block_name="test block") - csv_path = self._csv_path() - - result = fetch_db_data(csv_path, "settlement", "test_block", "42") - - self.assertTrue(result) - self.assertTrue(os.path.exists(csv_path)) - with open(csv_path) as f: - reader = csv.DictReader(f) - rows = list(reader) - self.assertEqual(len(rows), 1) - self.assertIn("latitude", rows[0]) - self.assertIn("longitude", rows[0]) - self.assertEqual(rows[0]["sett_id"], "SETT001") - self.assertEqual(rows[0]["sett_name"], "Test Settlement") - - def test_moderated_record_uses_moderated_json(self): - moderated_data = _make_settlement_json("42", "test block") - moderated_data["Settlements_name"] = "Moderated Settlement Name" - _create_settlement( - plan_id="42", - block_name="test block", - settlement_id="SETT002", - is_moderated=True, - data_override=moderated_data, - ) - csv_path = self._csv_path() - - result = fetch_db_data(csv_path, "settlement", "test_block", "42") - - self.assertTrue(result) - with open(csv_path) as f: - rows = list(csv.DictReader(f)) - self.assertEqual(len(rows), 1) - self.assertEqual(rows[0]["sett_name"], "Moderated Settlement Name") - - def test_deleted_records_excluded(self): - _create_settlement(plan_id="42", block_name="test block", is_deleted=True) - csv_path = self._csv_path() - - result = fetch_db_data(csv_path, "settlement", "test_block", "42") - - self.assertFalse(result) - self.assertFalse(os.path.exists(csv_path)) - - def test_rejected_submissions_excluded_by_transform(self): - _create_settlement( - plan_id="42", block_name="test block", - settlement_id="SETT003", review_state="rejected" - ) - csv_path = self._csv_path() - - result = fetch_db_data(csv_path, "settlement", "test_block", "42") - - self.assertFalse(result) - - def test_returns_false_for_no_matching_records(self): - csv_path = self._csv_path() - result = fetch_db_data(csv_path, "settlement", "nonexistent_block", "99") - self.assertFalse(result) - - def test_returns_false_for_unknown_resource_type(self): - csv_path = self._csv_path() - result = fetch_db_data(csv_path, "unknown_type", "test_block", "42") - self.assertFalse(result) - - def test_block_name_with_spaces_matches_underscore_block_param(self): - _create_settlement(plan_id="42", block_name="test block") - csv_path = self._csv_path() - - # block param uses underscore; DB has spaces — should still match - result = fetch_db_data(csv_path, "settlement", "test_block", "42") - - self.assertTrue(result) - - def test_only_matching_plan_id_returned(self): - _create_settlement(plan_id="42", block_name="test block", settlement_id="S1") - _create_settlement(plan_id="99", block_name="test block", settlement_id="S2") - csv_path = self._csv_path() - - result = fetch_db_data(csv_path, "settlement", "test_block", "42") - - self.assertTrue(result) - with open(csv_path) as f: - rows = list(csv.DictReader(f)) - self.assertEqual(len(rows), 1) - self.assertEqual(rows[0]["sett_id"], "S1") - - -class AddResourcesAPITest(APITestCase): - - def setUp(self): - self.client = APIClient() - self.url = reverse("add_resources") - - @patch("plans.api.build_layer", return_value=True) - @patch("plans.api.sync_form_type", return_value=True) - def test_returns_201_when_db_data_exists(self, mock_sync, mock_build): - _create_settlement(plan_id="42", block_name="test block") - - response = self.client.post(self.url, { - "layer_name": "test_layer", - "resource_type": "settlement", - "plan_id": "42", - "plan_name": "test plan", - "district_name": "test district", - "block_name": "test block", - }) - - self.assertEqual(response.status_code, status.HTTP_201_CREATED) - mock_sync.assert_called_once_with("settlement") - mock_build.assert_called_once() - - @patch("plans.api.sync_form_type", return_value=True) - def test_returns_404_when_no_db_data(self, mock_sync): - response = self.client.post(self.url, { - "layer_name": "test_layer", - "resource_type": "settlement", - "plan_id": "99", - "plan_name": "test plan", - "district_name": "test district", - "block_name": "no block", - }) - - self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) - - @patch("plans.api.build_layer", return_value=True) - @patch("plans.api.sync_form_type", return_value=False) - def test_proceeds_with_db_data_even_when_sync_fails(self, mock_sync, mock_build): - _create_settlement(plan_id="42", block_name="test block") - - response = self.client.post(self.url, { - "layer_name": "test_layer", - "resource_type": "settlement", - "plan_id": "42", - "plan_name": "test plan", - "district_name": "test district", - "block_name": "test block", - }) - - self.assertEqual(response.status_code, status.HTTP_201_CREATED) - mock_build.assert_called_once() - - @patch("plans.api.build_layer", return_value=False) - @patch("plans.api.sync_form_type", return_value=True) - def test_returns_500_when_build_layer_fails(self, mock_sync, mock_build): - _create_settlement(plan_id="42", block_name="test block") - - response = self.client.post(self.url, { - "layer_name": "test_layer", - "resource_type": "settlement", - "plan_id": "42", - "plan_name": "test plan", - "district_name": "test district", - "block_name": "test block", - }) - - self.assertEqual(response.status_code, status.HTTP_500_INTERNAL_SERVER_ERROR) +# plans/tests.py +import csv +import os +import tempfile +from datetime import datetime, timezone +from unittest.mock import patch + +from django.test import TestCase +from django.urls import reverse +from rest_framework.test import APITestCase, APIClient +from rest_framework import status + +from dpr.models import ODK_settlement +from .models import Plan +from .utils import fetch_db_data +from projects.models import Project, AppType +from organization.models import Organization +from users.models import User, UserProjectGroup +from django.contrib.auth.models import Group, Permission + + +class PlanModelTest(TestCase): + def setUp(self): + # Create organization + self.organization = Organization.objects.create(name="Test Organization") + + # Create project with app_type + self.project = Project.objects.create( + name="Test Project", + organization=self.organization, + app_type=AppType.WATERSHED, + enabled=True, + ) + + # Create user + self.user = User.objects.create_user( + username="testuser", + email="test@example.com", + password="password123", + organization=self.organization, + ) + + def test_plan_creation(self): + plan = Plan.objects.create( + name="Test Watershed Plan", + project=self.project, + organization=self.organization, + state="Test State", + district="Test District", + block="Test Block", + village="Test Village", + gram_panchayat="Test GP", + created_by=self.user, + ) + + # Check model attributes + self.assertEqual(plan.name, "Test Watershed Plan") + self.assertEqual(plan.project, self.project) + self.assertEqual(plan.organization, self.organization) + self.assertEqual(plan.state, "Test State") + self.assertEqual(plan.district, "Test District") + self.assertEqual(plan.created_by, self.user) + + +class PlanAPITest(APITestCase): + def setUp(self): + self.client = APIClient() + + # Create organization + self.organization = Organization.objects.create(name="Test Organization") + + # Create project with app_type + self.project = Project.objects.create( + name="Test Project", + organization=self.organization, + app_type=AppType.WATERSHED, + enabled=True, + ) + + # Create admin user + self.admin_user = User.objects.create_user( + username="admin", + email="admin@example.com", + password="password123", + organization=self.organization, + is_superadmin=True, + ) + + # Create edit user + self.edit_user = User.objects.create_user( + username="editor", + email="editor@example.com", + password="password123", + organization=self.organization, + ) + + # Create view user + self.view_user = User.objects.create_user( + username="viewer", + email="viewer@example.com", + password="password123", + organization=self.organization, + ) + + # Create groups and permissions + self.admin_group = Group.objects.create(name="Project Admin") + self.editor_group = Group.objects.create(name="Project Editor") + self.viewer_group = Group.objects.create(name="Project Viewer") + + # Create permissions + Permission.objects.get_or_create( + codename="view_watershed", + name="Can view watershed planning data", + content_type_id=1, # This would typically be correct content type ID + ) + + Permission.objects.get_or_create( + codename="add_watershed", + name="Can add watershed planning data", + content_type_id=1, + ) + + Permission.objects.get_or_create( + codename="change_watershed", + name="Can change watershed planning data", + content_type_id=1, + ) + + Permission.objects.get_or_create( + codename="delete_watershed", + name="Can delete watershed planning data", + content_type_id=1, + ) + + # Assign permissions to groups + view_perm = Permission.objects.get(codename="view_watershed") + add_perm = Permission.objects.get(codename="add_watershed") + change_perm = Permission.objects.get(codename="change_watershed") + delete_perm = Permission.objects.get(codename="delete_watershed") + + self.admin_group.permissions.add(view_perm, add_perm, change_perm, delete_perm) + self.editor_group.permissions.add(view_perm, add_perm, change_perm) + self.viewer_group.permissions.add(view_perm) + + # Assign users to project roles + UserProjectGroup.objects.create( + user=self.edit_user, project=self.project, group=self.editor_group + ) + + UserProjectGroup.objects.create( + user=self.view_user, project=self.project, group=self.viewer_group + ) + + # Create a test plan + self.plan = Plan.objects.create( + name="Test Watershed Plan", + project=self.project, + organization=self.organization, + state="Test State", + district="Test District", + block="Test Block", + village="Test Village", + gram_panchayat="Test GP", + created_by=self.admin_user, + ) + + # URLs + self.plans_list_url = reverse( + "project-plan-list", kwargs={"project_pk": self.project.pk} + ) + self.plan_detail_url = reverse( + "project-plan-detail", + kwargs={"project_pk": self.project.pk, "pk": self.plan.pk}, + ) + + def test_list_plans_as_admin(self): + self.client.force_authenticate(user=self.admin_user) + response = self.client.get(self.plans_list_url) + + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(len(response.data), 1) + + def test_list_plans_as_editor(self): + self.client.force_authenticate(user=self.edit_user) + response = self.client.get(self.plans_list_url) + + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(len(response.data), 1) + + def test_list_plans_as_viewer(self): + self.client.force_authenticate(user=self.view_user) + response = self.client.get(self.plans_list_url) + + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(len(response.data), 1) + + def test_create_plan_as_admin(self): + self.client.force_authenticate(user=self.admin_user) + data = { + "name": "New Watershed Plan", + "state": "New State", + "district": "New District", + "block": "New Block", + "village": "New Village", + "gram_panchayat": "New GP", + } + + response = self.client.post(self.plans_list_url, data) + + self.assertEqual(response.status_code, status.HTTP_201_CREATED) + self.assertEqual(response.data["name"], "New Watershed Plan") + self.assertEqual(response.data["state"], "New State") + + # Check plan was created in database + self.assertEqual(Plan.objects.count(), 2) + + def test_create_plan_as_editor(self): + self.client.force_authenticate(user=self.edit_user) + data = { + "name": "Editor Plan", + "state": "Editor State", + "district": "Editor District", + "block": "Editor Block", + "village": "Editor Village", + "gram_panchayat": "Editor GP", + } + + response = self.client.post(self.plans_list_url, data) + + # Editor should be able to create plans + self.assertEqual(response.status_code, status.HTTP_201_CREATED) + self.assertEqual(response.data["name"], "Editor Plan") + + def test_create_plan_as_viewer(self): + self.client.force_authenticate(user=self.view_user) + data = { + "name": "Viewer Plan", + "state": "Viewer State", + "district": "Viewer District", + "block": "Viewer Block", + "village": "Viewer Village", + "gram_panchayat": "Viewer GP", + } + + response = self.client.post(self.plans_list_url, data) + + # Viewer should not be able to create plans + self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) + + def test_update_plan_as_admin(self): + self.client.force_authenticate(user=self.admin_user) + data = { + "name": "Updated Plan", + "state": "Updated State", + "district": "Updated District", + "block": "Updated Block", + "village": "Updated Village", + "gram_panchayat": "Updated GP", + } + + response = self.client.put(self.plan_detail_url, data) + + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(response.data["name"], "Updated Plan") + + # Verify database was updated + updated_plan = Plan.objects.get(pk=self.plan.pk) + self.assertEqual(updated_plan.name, "Updated Plan") + + def test_update_plan_as_editor(self): + self.client.force_authenticate(user=self.edit_user) + data = { + "name": "Editor Updated", + "state": self.plan.state, + "district": self.plan.district, + "block": self.plan.block, + "village": self.plan.village, + "gram_panchayat": self.plan.gram_panchayat, + } + + response = self.client.patch(self.plan_detail_url, {"name": "Editor Updated"}) + + # Editor should be able to update plans + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(response.data["name"], "Editor Updated") + + def test_update_plan_as_viewer(self): + self.client.force_authenticate(user=self.view_user) + data = {"name": "Viewer Updated"} + + response = self.client.patch(self.plan_detail_url, data) + + # Viewer should not be able to update plans + self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) + + def test_delete_plan_as_admin(self): + self.client.force_authenticate(user=self.admin_user) + response = self.client.delete(self.plan_detail_url) + + self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT) + + # Verify plan was deleted + self.assertEqual(Plan.objects.count(), 0) + + def test_delete_plan_as_editor(self): + self.client.force_authenticate(user=self.edit_user) + response = self.client.delete(self.plan_detail_url) + + # Editor should not be able to delete plans + self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) + + # Verify plan was not deleted + self.assertEqual(Plan.objects.count(), 1) + + def test_delete_plan_as_viewer(self): + self.client.force_authenticate(user=self.view_user) + response = self.client.delete(self.plan_detail_url) + + # Viewer should not be able to delete plans + self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) + + # Verify plan was not deleted + self.assertEqual(Plan.objects.count(), 1) + + +# Minimal valid ODK settlement JSON (same structure as ODK submissions stored in data_settlement) +def _make_settlement_json(plan_id, block_name, settlement_id="SETT001", review_state="hasIssues"): + return { + "__id": f"uuid:{settlement_id}", + "__system": {"reviewState": review_state, "submissionDate": "2024-01-01T00:00:00Z"}, + "block_name": block_name, + "plan_id": str(plan_id), + "GPS_point": { + "point_mapsappearance": { + "coordinates": [78.5, 20.5] + } + }, + "Settlements_id": settlement_id, + "Settlements_name": "Test Settlement", + "MNREGA_INFORMATION": { + "NREGA_aware": 10, + "NREGA_applied": 5, + "NREGA_job_card": 3, + "total_household": 2, + "NREGA_work_days": 100, + "q1": "yes", + "select_one_Y_N": "yes", + "select_one_demands": "wages", + "select_multiple_issues": "delayed_payment", + "select_one_contributions": "labour", + }, + } + + +def _create_settlement(plan_id, block_name, settlement_id="SETT001", + is_deleted=False, is_moderated=False, review_state="hasIssues", + data_override=None): + data = data_override or _make_settlement_json(plan_id, block_name, settlement_id, review_state) + return ODK_settlement.objects.create( + settlement_id=settlement_id, + settlement_name="Test Settlement", + submission_time=datetime(2024, 1, 1, tzinfo=timezone.utc), + submitted_by="test_user", + status_re=review_state, + latitude=20.5, + longitude=78.5, + block_name=block_name, + number_of_households=10, + largest_caste="General", + smallest_caste="SC", + settlement_status="active", + plan_id=str(plan_id), + plan_name="Test Plan", + uuid=f"uuid:{settlement_id}", + farmer_family={}, + livestock_census={}, + nrega_job_aware=10, + nrega_job_applied=5, + nrega_past_work="yes", + nrega_raise_demand="yes", + nrega_demand="wages", + nrega_issues="delayed_payment", + nrega_community="labour", + data_settlement=data, + is_deleted=is_deleted, + is_moderated=is_moderated, + ) + + +class FetchDbDataTest(TestCase): + + def setUp(self): + self.tmp_dir = tempfile.mkdtemp() + + def _csv_path(self, name="test.csv"): + return os.path.join(self.tmp_dir, name) + + def test_returns_true_and_writes_csv_for_valid_settlement(self): + _create_settlement(plan_id="42", block_name="test block") + csv_path = self._csv_path() + + result = fetch_db_data(csv_path, "settlement", "test_block", "42") + + self.assertTrue(result) + self.assertTrue(os.path.exists(csv_path)) + with open(csv_path) as f: + reader = csv.DictReader(f) + rows = list(reader) + self.assertEqual(len(rows), 1) + self.assertIn("latitude", rows[0]) + self.assertIn("longitude", rows[0]) + self.assertEqual(rows[0]["sett_id"], "SETT001") + self.assertEqual(rows[0]["sett_name"], "Test Settlement") + + def test_moderated_record_uses_moderated_json(self): + moderated_data = _make_settlement_json("42", "test block") + moderated_data["Settlements_name"] = "Moderated Settlement Name" + _create_settlement( + plan_id="42", + block_name="test block", + settlement_id="SETT002", + is_moderated=True, + data_override=moderated_data, + ) + csv_path = self._csv_path() + + result = fetch_db_data(csv_path, "settlement", "test_block", "42") + + self.assertTrue(result) + with open(csv_path) as f: + rows = list(csv.DictReader(f)) + self.assertEqual(len(rows), 1) + self.assertEqual(rows[0]["sett_name"], "Moderated Settlement Name") + + def test_deleted_records_excluded(self): + _create_settlement(plan_id="42", block_name="test block", is_deleted=True) + csv_path = self._csv_path() + + result = fetch_db_data(csv_path, "settlement", "test_block", "42") + + self.assertFalse(result) + self.assertFalse(os.path.exists(csv_path)) + + def test_rejected_submissions_excluded_by_transform(self): + _create_settlement( + plan_id="42", block_name="test block", + settlement_id="SETT003", review_state="rejected" + ) + csv_path = self._csv_path() + + result = fetch_db_data(csv_path, "settlement", "test_block", "42") + + self.assertFalse(result) + + def test_returns_false_for_no_matching_records(self): + csv_path = self._csv_path() + result = fetch_db_data(csv_path, "settlement", "nonexistent_block", "99") + self.assertFalse(result) + + def test_returns_false_for_unknown_resource_type(self): + csv_path = self._csv_path() + result = fetch_db_data(csv_path, "unknown_type", "test_block", "42") + self.assertFalse(result) + + def test_block_name_with_spaces_matches_underscore_block_param(self): + _create_settlement(plan_id="42", block_name="test block") + csv_path = self._csv_path() + + # block param uses underscore; DB has spaces — should still match + result = fetch_db_data(csv_path, "settlement", "test_block", "42") + + self.assertTrue(result) + + def test_only_matching_plan_id_returned(self): + _create_settlement(plan_id="42", block_name="test block", settlement_id="S1") + _create_settlement(plan_id="99", block_name="test block", settlement_id="S2") + csv_path = self._csv_path() + + result = fetch_db_data(csv_path, "settlement", "test_block", "42") + + self.assertTrue(result) + with open(csv_path) as f: + rows = list(csv.DictReader(f)) + self.assertEqual(len(rows), 1) + self.assertEqual(rows[0]["sett_id"], "S1") + + +class AddResourcesAPITest(APITestCase): + + def setUp(self): + self.client = APIClient() + self.url = reverse("add_resources") + + @patch("plans.api.build_layer", return_value=True) + @patch("plans.api.sync_form_type", return_value=True) + def test_returns_201_when_db_data_exists(self, mock_sync, mock_build): + _create_settlement(plan_id="42", block_name="test block") + + response = self.client.post(self.url, { + "layer_name": "test_layer", + "resource_type": "settlement", + "plan_id": "42", + "plan_name": "test plan", + "district_name": "test district", + "block_name": "test block", + }) + + self.assertEqual(response.status_code, status.HTTP_201_CREATED) + mock_sync.assert_called_once_with("settlement") + mock_build.assert_called_once() + + @patch("plans.api.sync_form_type", return_value=True) + def test_returns_404_when_no_db_data(self, mock_sync): + response = self.client.post(self.url, { + "layer_name": "test_layer", + "resource_type": "settlement", + "plan_id": "99", + "plan_name": "test plan", + "district_name": "test district", + "block_name": "no block", + }) + + self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) + + @patch("plans.api.build_layer", return_value=True) + @patch("plans.api.sync_form_type", return_value=False) + def test_proceeds_with_db_data_even_when_sync_fails(self, mock_sync, mock_build): + _create_settlement(plan_id="42", block_name="test block") + + response = self.client.post(self.url, { + "layer_name": "test_layer", + "resource_type": "settlement", + "plan_id": "42", + "plan_name": "test plan", + "district_name": "test district", + "block_name": "test block", + }) + + self.assertEqual(response.status_code, status.HTTP_201_CREATED) + mock_build.assert_called_once() + + @patch("plans.api.build_layer", return_value=False) + @patch("plans.api.sync_form_type", return_value=True) + def test_returns_500_when_build_layer_fails(self, mock_sync, mock_build): + _create_settlement(plan_id="42", block_name="test block") + + response = self.client.post(self.url, { + "layer_name": "test_layer", + "resource_type": "settlement", + "plan_id": "42", + "plan_name": "test plan", + "district_name": "test district", + "block_name": "test block", + }) + + self.assertEqual(response.status_code, status.HTTP_500_INTERNAL_SERVER_ERROR) diff --git a/plans/urls.py b/plans/urls.py index 9beec875..675bc64a 100644 --- a/plans/urls.py +++ b/plans/urls.py @@ -46,4 +46,5 @@ path("", include(org_watershed_router.urls)), path("", include(global_router.urls)), path("map_plan_to_gp/", api.map_plan_to_gp, name="map_plan_to_gp"), + path("plan_count/", api.plan_count, name="plan_count"), ] diff --git a/plans/utils.py b/plans/utils.py index 7e96cfcf..f4e0151f 100755 --- a/plans/utils.py +++ b/plans/utils.py @@ -1,739 +1,739 @@ -import csv -import logging -import re -from datetime import datetime, timezone - -import dateutil.parser -import requests - -from dpr.models import ( - ODK_settlement, ODK_well, ODK_waterbody, - ODK_groundwater, ODK_agri, ODK_livelihood, ODK_crop, - SWB_maintenance, SWB_RS_maintenance, GW_maintenance, Agri_maintenance, - ODK_agrohorticulture, -) -from moderation.utils.utils import ( - MODEL_FIELD_EXTRACTORS as _MODERATION_EXTRACTORS, - extract_lat_lon_from_gps, -) -from utilities.constants import ODK_URL_SESSION - -logger = logging.getLogger(__name__) - -_token_cache = { - "token": None, - "expires_at": None, -} - -# MARK: Helper -def normalize_name(name): - """ - Normalize names for comparison by: - - Converting to lowercase - - Replacing spaces with underscores - - Removing extra whitespace - """ - if not name: - return "" - return name.lower().replace(" ", "_").strip() - -_RESOURCE_TYPES_FLAT_HEADER = frozenset({ - "settlement", "well", "waterbody", "cropping", -}) -_RESOURCE_TYPES_UNION_HEADER = frozenset({ - "plan_gw", "plan_agri", "main_swb", "main_gw", "main_swb_rs", "main_agri", - "livelihood", "agrohorticulture", -}) - - -def _write_csv(resource_type, modified_response_list, all_keys, csv_path): - if resource_type in _RESOURCE_TYPES_FLAT_HEADER: - header_keys = modified_response_list[0].keys() - with open(csv_path, "w", encoding="utf-8") as output_file: - dict_writer = csv.DictWriter( - output_file, fieldnames=header_keys, extrasaction="ignore" - ) - dict_writer.writeheader() - dict_writer.writerows(modified_response_list) - elif resource_type in _RESOURCE_TYPES_UNION_HEADER: - with open(csv_path, "w", newline="", encoding="utf-8") as csvfile: - dict_writer = csv.DictWriter(csvfile, fieldnames=list(all_keys)) - dict_writer.writeheader() - for item in modified_response_list: - dict_writer.writerow(flatten_dict(item)) - logger.info(f"CSV generated for '{resource_type}' at {csv_path}") - - -# MARK: Modify ODK Settlement Data -def modify_response_list_settlement(res, block, plan_id): - res_list = [] - logger.info( - "modify_response_list_settlement: block=%s plan_id=%s input_records=%d", - block, plan_id, len(res) if res else 0, - ) - for result in res: - if result is None: - continue - - if result.get("__system", {}).get("reviewState") == "rejected": - continue - - try: - if normalize_name(result.get("block_name").lower()) != normalize_name(block): - continue - except AttributeError: - continue - - if str(result.get("plan_id")) != str(plan_id): - continue - - latitude, longitude = extract_lat_lon_from_gps(result.get("GPS_point")) - if latitude is not None and longitude is not None: - result["latitude"] = latitude - result["longitude"] = longitude - - result["status_re"] = result["__system"]["reviewState"] - result["sett_id"] = result["Settlements_id"] - result["sett_name"] = result["Settlements_name"] - try: - mgnrega_info = result.get("MNREGA_INFORMATION", {}) - except Exception: - logger.exception("modify_response_list_settlement: failed reading MNREGA_INFORMATION") - continue - if mgnrega_info: - result["job_aware"] = mgnrega_info.get("NREGA_aware", "") or 0 - result["job_applied"] = mgnrega_info.get("NREGA_applied", "") or 0 - result["job_card"] = mgnrega_info.get("NREGA_job_card", "") or 0 - result["without_jc"] = mgnrega_info.get("total_household", "") or 0 - result["work_days"] = mgnrega_info.get("NREGA_work_days", "") or 0 - result["past_work"] = mgnrega_info.get("q1", "") or "0" - result["raise_demand"] = mgnrega_info.get("select_one_Y_N", "") or "0" - result["demand"] = mgnrega_info.get("select_one_demands", "") or "0" - result["issues"] = mgnrega_info.get("select_multiple_issues", "") or "0" - result["community"] = ( - mgnrega_info.get("select_one_contributions", "") or "0" - ) - res_list.append(result) - return res_list - - -# MARK: Modify ODK Well Data -def modify_response_list_well(res, block, plan_id): - res_list = [] - for result in res: - if result is None: - continue - - if result.get("__system", {}).get("reviewState") == "rejected": - continue - - try: - if normalize_name(result.get("block_name").lower()) != normalize_name(block): - continue - except AttributeError: - continue - - if str(result.get("plan_id")) != str(plan_id): - continue - - latitude, longitude = extract_lat_lon_from_gps(result.get("GPS_point")) - if latitude is not None and longitude is not None: - result["latitude"] = latitude - result["longitude"] = longitude - - result["status_re"] = result["__system"]["reviewState"] - result["well_id"] = result["well_id"] - - well_usage_section = result.get("Well_usage", {}) - try: - result["ben_settlement"] = result.get("beneficiary_settlement", "") or "NA" - result["owner"] = result.get("select_one_owns", "") or "NA" - result["hh_benefitted"] = result.get("households_benefited", "") or "NA" - result["caste"] = result.get("select_multiple_caste_use", "") or "NA" - result["functional"] = ( - well_usage_section.get("select_one_Functional_Non_functional", "") - or "NA" - ) - result["need_maintenance"] = ( - well_usage_section.get("select_one_maintenance", "") or "NA" - ) - repair_value = well_usage_section.get("select_one_repairs_well") - if repair_value: - repair_value = str(repair_value).lower() - if repair_value == "other": - result["repair"] = ( - well_usage_section.get("select_one_repairs_well_other", "") - or "NA" - ) - else: - result["repair"] = repair_value - else: - result["repair"] = "NA" - except Exception: - logger.exception("modify_response_list_well: failed enriching record") - continue - res_list.append(result) - - return res_list - - -# MARK: Modify ODK Waterbody Data -def modify_response_list_waterbody(res, block, plan_id): - res_list = [] - logger.info( - "modify_response_list_waterbody: block=%s plan_id=%s input_records=%d", - block, plan_id, len(res) if res else 0, - ) - for result in res: - if result is None: - continue - if result.get("__system", {}).get("reviewState") == "rejected": - continue - try: - if normalize_name(result.get("block_name").lower()) != normalize_name(block): - continue - except AttributeError: - continue - if str(result.get("plan_id")) != str(plan_id): - continue - - latitude, longitude = extract_lat_lon_from_gps(result.get("GPS_point")) - if latitude is not None and longitude is not None: - result["latitude"] = latitude - result["longitude"] = longitude - - result["status_re"] = result["__system"]["reviewState"] - result["wb_id"] = result["waterbodies_id"] - - # type_of_water_st = result["select_one_water_structure"] - # if type_of_water_st: - # type_of_water_st = str(type_of_water_st).lower() - # if type_of_water_st == "other": - # result["wbs_type"] = result.get("select_one_water_structure_other", "") or "" - # else: - # result["wbs_type"] = result.get("select_one_water_structure", "") or "" - # else: - # result["wbs_type"] = "0" - - result["wbs_type"] = result.get("select_one_water_structure", "") or "0" - - try: - manager = result["select_one_manages"] - if manager: - manager = str(manager).lower() - if manager == "other": - result["who_manages"] = result.get("text_one_manages", "") or "" - else: - result["who_manages"] = result.get("select_one_manages", "") or "" - else: - result["who_manages"] = "0" - - who_owns = result["select_one_owns"] - if who_owns: - who_owns = str(who_owns).lower() - if who_owns == "other" or who_owns == "any other": - result["owner"] = result.get("text_one_owns", "") or "" - else: - result["owner"] = result.get("select_one_owns", "") or "" - else: - result["owner"] = "0" - result["caste"] = result.get("select_multiple_caste_use", "") or "0" - result["hh_benefitted"] = result.get("households_benefited", "") or 0 - result["identified"] = result.get("select_one_identified", "") or "0" - result["need_maintenance"] = result.get("select_one_maintenance") or "0" - - # Handle the dynamic water structure dimensions - # water_structure_type = result.get("select_one_water_structure", "").lower().replace("_", " ") - # water_structure_dimension = {} - # for key, value in result.items(): - # if isinstance(value, dict): - # structure_type = key.lower().replace("_", " ") - # if structure_type == water_structure_type: - # water_structure_dimension = { - # "length": next((v for k, v in value.items() if k.startswith("Length")), None), - # "breadth": next((v for k, v in value.items() if k.startswith("Breadth")), None), - # "width": next((v for k, v in value.items() if k.startswith("Width")), None), - # "depth": next((v for k, v in value.items() if k.startswith("Depth")), None), - # "height": next((v for k, v in value.items() if k.startswith("Height")), None), - # } - # break - - # Add the dimensions to the result dictionary - # result.update(water_structure_dimension) - except Exception: - logger.exception("modify_response_list_waterbody: failed enriching record") - continue - res_list.append(result) - return res_list - - -# MARK: Modify ODK Cropping Data -def modify_reponse_list_cropping(res, block, plan_id): - res_list = [] - logger.info( - "modify_reponse_list_cropping: block=%s plan_id=%s input_records=%d", - block, plan_id, len(res) if res else 0, - ) - - for result in res: - if result is None: - continue - - if result.get("__system", {}).get("reviewState") == "rejected": - continue - - try: - if normalize_name(result.get("block_name").lower()) != normalize_name(block): - continue - except AttributeError: - continue - - - if str(result.get("plan_id")) != str(plan_id): - continue - - - latitude, longitude = extract_lat_lon_from_gps(result.get("GPS_point")) - if latitude is not None and longitude is not None: - result["latitude"] = latitude - result["longitude"] = longitude - - result["status_re"] = result["__system"]["reviewState"] - result["crop_id"] = result["__id"] - - # Settlement and land basic information (5 keys) - result["sett_name"] = result.get("beneficiary_settlement", "") or "" - result["uncropp_br"] = result.get("Uncropped_barren_land", "") or "" - result["irrigatn"] = result.get("select_multiple_widgets", "") or "" - result["land_cls"] = result.get("select_one_classified", "") or "" - result["crop_seas"] = result.get("select_one_practice", "") or "" - - # Kharif season information (3 keys) - result["crop_khrf"] = result.get("select_multiple_cropping_kharif", "") or "" - result["crop_kh_o"] = result.get("select_multiple_cropping_kharif_other", "") or "" - result["area_khrf"] = result.get("total_area_cultivation_kharif", "") or "" - - # Rabi season information (3 keys) - result["crops_rabi"] = result.get("select_multiple_cropping_Rabi", "") or "" - result["crop_rb_o"] = result.get("select_multiple_cropping_Rabi_other", "") or "" - result["area_rabi"] = result.get("total_area_cultivation_Rabi", "") or "" - - # Zaid season information (3 keys) - result["crops_zaid"] = result.get("select_multiple_cropping_Zaid", "") or "" - result["crop_zd_o"] = result.get("select_multiple_cropping_Zaid_other", "") or "" - result["area_zaid"] = result.get("total_area_cultivation_Zaid", "") or "" - - # Soil and productivity information (4 keys) - result["productiv"] = result.get("select_one_productivity", "") or "" - result["soil_deg"] = result.get("soil_degraded", "") or "" - result["deg_reas"] = result.get("select_one_reason_degradation", "") or "" - result["deg_reas2"] = result.get("select_one_reason_degradation_1", "") or "" - - res_list.append(result) - - return res_list - - -# MARK: Modify ODK Plan Data -def modify_response_list_plan(res, block, plan_id): - res_list = [] - for result in res: - if result is None: - continue - - if result.get("__system", {}).get("reviewState") == "rejected": - continue - - try: - if normalize_name(result.get("block_name").lower()) != normalize_name(block): - continue - except AttributeError: - continue - - if str(result.get("plan_id")) != str(plan_id): - continue - - latitude, longitude = extract_lat_lon_from_gps(result.get("GPS_point")) - if latitude is not None and longitude is not None: - result["latitude"] = latitude - result["longitude"] = longitude - - result["status_re"] = result["__system"]["reviewState"] - result["work_id"] = result["work_id"] - - work_type = None - selected_work = None - - if "TYPE_OF_WORK" in result: - work_type = result["TYPE_OF_WORK"] - elif "TYPE_OF_WORK_ID" in result: - work_type = result["TYPE_OF_WORK_ID"] - - if work_type: - result["work_type"] = work_type - - work_type_key = re.sub(r"[^a-zA-Z0-9]+", "_", work_type) - - if work_type_key in result: - selected_work = result[work_type_key] - if selected_work: - result["selected_work"] = selected_work - else: - result["selected_work"] = work_type_key - elif work_type in result: - selected_work = result[work_type] - if selected_work: - result["selected_work"] = selected_work - else: - result["selected_work"] = work_type - else: - result["selected_work"] = work_type - - result["ben_settlement"] = result["beneficiary_settlement"] - result["ben_name"] = result["Beneficiary_Name"] - result["ben_contact"] = result["Beneficiary_Contact_Number"] - res_list.append(result) - - return res_list - - -# MARK: Modify ODK Livelihood Data -def modify_response_list_livelihood(res, block, plan_id): - res_list = [] - for result in res: - # if result["__system"]["reviewState"] != "rejected": - if result is None: - continue - - if result.get("__system", {}).get("reviewState") == "rejected": - continue - - try: - if normalize_name(result.get("block_name").lower()) != normalize_name(block): - continue - except AttributeError: - continue - - if str(result.get("plan_id")) != str(plan_id): - continue - - latitude, longitude = extract_lat_lon_from_gps(result.get("GPS_point")) - if latitude is not None and longitude is not None: - result["latitude"] = latitude - result["longitude"] = longitude - - result["status_re"] = result["__system"]["reviewState"] - res_list.append(result) - return res_list - - -# MARK: Modify ODK Maintenance / Agrohorticulture (Generic) -def modify_response_list_work(res, block, plan_id): - """ - Robust transform for maintenance and agrohorticulture submissions. - Unlike `modify_response_list_plan`, this avoids bracket-access on keys - that maintenance/agrohorticulture blobs may not carry (e.g. work_id, - Beneficiary_Name). Block filter is best-effort: applied only when the - blob actually has block_name (these models have no block_name DB column). - """ - res_list = [] - for result in res: - if result is None: - continue - if not isinstance(result, dict): - continue - if result.get("__system", {}).get("reviewState") == "rejected": - continue - - blob_block = result.get("block_name") - if blob_block: - try: - if normalize_name(str(blob_block).lower()) != normalize_name(block): - continue - except AttributeError: - continue - - if str(result.get("plan_id")) != str(plan_id): - continue - - lat, lon = extract_lat_lon_from_gps(result.get("GPS_point")) - if lat is not None and lon is not None: - result["latitude"] = lat - result["longitude"] = lon - - sys_info = result.get("__system") or {} - if isinstance(sys_info, dict): - review_state = sys_info.get("reviewState") - if review_state: - result["status_re"] = review_state - - res_list.append(result) - return res_list - - -# Layer-build source-of-truth registry. Key = resource_type / work_type the -# `/add_resources` and `/add_works` endpoints accept. Tuple = (Model, JSON -# blob field, model has block_name column for DB-level pre-filtering). -# -# Resources (workspace=resources): settlement, well, waterbody, cropping -# Works (workspace=works): -# plan_gw — new recharge structures (groundwater) -# main_gw — maintenance of recharge structures -# plan_agri — new irrigation structures -# main_agri — maintenance of irrigation structures -# main_swb — surface water body maintenance (water-structure form) -# main_swb_rs — remote-sensed surface water body maintenance -# livelihood — livelihood -# agrohorticulture — agrohorticulture -_DB_CONFIG = { - "settlement": (ODK_settlement, "data_settlement", True), - "well": (ODK_well, "data_well", True), - "waterbody": (ODK_waterbody, "data_waterbody", True), - "cropping": (ODK_crop, "data_crop", False), - "plan_gw": (ODK_groundwater, "data_groundwater", True), - "plan_agri": (ODK_agri, "data_agri", True), - "livelihood": (ODK_livelihood, "data_livelihood", True), - "main_swb": (SWB_maintenance, "data_swb_maintenance", False), - "main_gw": (GW_maintenance, "data_gw_maintenance", False), - "main_swb_rs": (SWB_RS_maintenance, "data_swb_rs_maintenance", False), - "main_agri": (Agri_maintenance, "data_agri_maintenance", False), - # `data_agohorticulture` is the actual model field name — there is a - # spelling typo in the schema. Respecting it here to avoid a migration. - "agrohorticulture": (ODK_agrohorticulture, "data_agohorticulture", False), -} - -# Fields never useful to project alongside the JSON blob: the blob itself, -# soft-delete metadata, and relational fields (FKs serialise poorly via values()). -_PROJECTION_EXCLUDED_FIELDS = frozenset({ - "data_before_moderation", - "is_deleted", - "deleted_at", - "deleted_by", - "moderated_by", -}) - - -def _scalar_projection_fields(model, json_blob_field: str) -> list: - """ - Concrete, non-relational fields on `model` safe to project via `.values()` - alongside the raw ODK JSON blob. Captures everything moderation can edit - (settlement_name, block_name, nrega_*, lat/lon, status_re, ...) so the - generated layer reflects the latest moderated values, not just the - original ODK submission stored in `data_`. - """ - skip = {json_blob_field, *_PROJECTION_EXCLUDED_FIELDS} - fields = [] - for f in model._meta.get_fields(): - if not getattr(f, "concrete", False): - continue - if f.many_to_one or f.one_to_one or f.many_to_many or f.one_to_many: - continue - if f.name in skip: - continue - fields.append(f.name) - return fields - - -def _merge_moderated(blob: dict, friendly: dict, friendly_canonical: bool) -> dict: - """ - Merge friendly DB column values into the raw ODK blob so the layer carries - both the original ODK keys (Settlements_name, GPS_point, ...) and the - moderated friendly columns (settlement_name, block_name, ...). - - `friendly_canonical=True` (model has a moderation extractor): friendly - columns are kept in sync with every moderation edit, so they win on - collision. `False` (no extractor, e.g. SWB_maintenance): moderation only - touches the blob, so the blob wins on collision. - - GeoPackage/SQLite (the downstream layer format) is case-insensitive on - column names, so a blob key like "GPS_point" and a friendly column - "gps_point" would collide on write. We dedupe case-insensitively in - favour of the canonical side; non-colliding keys (e.g. "Settlements_name" - vs "settlement_name") are both preserved. - """ - if friendly_canonical: - winner, loser = friendly, blob - else: - winner, loser = blob, friendly - - winner_lower = {k.lower() for k in winner} - loser_filtered = { - k: v for k, v in loser.items() - if k.lower() not in winner_lower - } - return {**loser_filtered, **winner} - - -# MARK: Fetch DB Data -def fetch_db_data(csv_path, resource_type, block, plan_id) -> int: - """ - Build the CSV of records for the given (resource_type, plan_id, block) - by reading from our DB (post-moderation source of truth). - - Returns the number of rows actually written to the CSV; 0 means no - usable data was found and the caller should treat it as a soft 404. - """ - logger.info( - f"fetch_db_data: starting — resource_type={resource_type}, " - f"plan_id={plan_id}, block={block}, csv_path={csv_path}" - ) - - entry = _DB_CONFIG.get(resource_type) - if not entry: - logger.warning(f"fetch_db_data: unknown resource_type '{resource_type}'") - return 0 - - model, data_field, has_block_col = entry - projection_fields = _scalar_projection_fields(model, data_field) - friendly_canonical = model in _MODERATION_EXTRACTORS - logger.info( - f"fetch_db_data: querying {model.__name__}.{data_field} " - f"with plan_id={plan_id}, is_deleted=False" - + ( - f", block_name icontains '{block}'" - if has_block_col - else " (no block_name column, skipping DB block filter)" - ) - ) - logger.info( - f"fetch_db_data: projecting blob '{data_field}' + " - f"{len(projection_fields)} moderated column(s) " - f"(friendly_canonical={friendly_canonical}): {projection_fields}" - ) - - qs = model.objects.filter(plan_id=str(plan_id), is_deleted=False) - if has_block_col: - qs = qs.filter(block_name__icontains=block.replace("_", " ").strip()) - - raw_rows = list(qs.values(data_field, *projection_fields)) - logger.info( - f"fetch_db_data: DB returned {len(raw_rows)} record(s) for " - f"resource_type={resource_type}, plan_id={plan_id}" - ) - - response_list = [] - empty_blob_count = 0 - for row in raw_rows: - blob = row.get(data_field) or {} - if not blob: - empty_blob_count += 1 - continue - friendly = {k: v for k, v in row.items() if k != data_field} - response_list.append(_merge_moderated(blob, friendly, friendly_canonical)) - - if empty_blob_count: - logger.warning( - f"fetch_db_data: skipped {empty_blob_count} record(s) with empty " - f"{data_field}" - ) - - if not response_list: - logger.warning( - f"fetch_db_data: no usable records for resource_type={resource_type}, " - f"plan_id={plan_id}, block={block}" - ) - return 0 - - logger.info( - f"fetch_db_data: running transform for resource_type={resource_type} " - f"on {len(response_list)} record(s) (each enriched with " - f"{len(projection_fields)} moderated column(s))" - ) - - all_keys = set() - if resource_type == "settlement": - rows = modify_response_list_settlement(response_list, block, plan_id) - elif resource_type == "well": - rows = modify_response_list_well(response_list, block, plan_id) - elif resource_type == "waterbody": - rows = modify_response_list_waterbody(response_list, block, plan_id) - elif resource_type == "cropping": - rows = modify_reponse_list_cropping(response_list, block, plan_id) - elif resource_type in ["plan_gw", "main_swb", "plan_agri"]: - rows = modify_response_list_plan(response_list, block, plan_id) - for item in rows: - all_keys.update(extract_keys(item)) - elif resource_type == "livelihood": - rows = modify_response_list_livelihood(response_list, block, plan_id) - for item in rows: - all_keys.update(extract_keys(item)) - elif resource_type in [ - "main_gw", "main_swb_rs", "main_agri", "agrohorticulture", - ]: - rows = modify_response_list_work(response_list, block, plan_id) - for item in rows: - all_keys.update(extract_keys(item)) - else: - logger.warning( - f"fetch_db_data: no transform defined for resource_type='{resource_type}'" - ) - return 0 - - logger.info( - f"fetch_db_data: transform produced {len(rows)} row(s) " - f"(filtered from {len(response_list)}) for resource_type={resource_type}" - ) - - if not rows: - logger.warning( - f"fetch_db_data: transform returned empty list for " - f"resource_type={resource_type}, plan_id={plan_id}, block={block}" - ) - return 0 - - logger.info(f"fetch_db_data: writing CSV to {csv_path}") - _write_csv(resource_type, rows, all_keys, csv_path) - logger.info( - f"fetch_db_data: done — {len(rows)} row(s) written to {csv_path} " - f"(columns include {len(projection_fields)} moderated friendly field(s))" - ) - return len(rows) - - -def flatten_dict(d, parent_key="", sep="_"): - items = [] - for k, v in d.items(): - new_key = f"{parent_key}{sep}{k}" if parent_key else k - if isinstance(v, dict): - items.extend(flatten_dict(v, new_key, sep=sep).items()) - else: - items.append((new_key, v)) - return dict(items) - - -def extract_keys(d, parent_key="", sep="_"): - keys = [] - for k, v in d.items(): - new_key = f"{parent_key}{sep}{k}" if parent_key else k - keys.append(new_key) - if isinstance(v, dict): - keys.extend(extract_keys(v, new_key, sep=sep)) - return keys - - -# MARK: Bearer Token -def fetch_bearer_token(email: str, password: str) -> str: - try: - if _token_cache["token"] and _token_cache["expires_at"]: - now = datetime.now(timezone.utc) - if now < _token_cache["expires_at"]: - return _token_cache["token"] - - response = requests.post( - ODK_URL_SESSION, json={"email": email, "password": password} - ) - print("Response: ", response) - if response.status_code == 200: - response_data = response.json() - _token_cache["token"] = response_data.get("token") - _token_cache["expires_at"] = dateutil.parser.parse( - response_data.get("expiresAt") - ) - return _token_cache["token"] - else: - raise Exception( - f"Failed to fetch bearer token. Status code: {response.status_code}" - ) - except Exception as e: - print(f"An error occurred while fetching the bearer token: {str(e)}") - raise +import csv +import logging +import re +from datetime import datetime, timezone + +import dateutil.parser +import requests + +from dpr.models import ( + ODK_settlement, ODK_well, ODK_waterbody, + ODK_groundwater, ODK_agri, ODK_livelihood, ODK_crop, + SWB_maintenance, SWB_RS_maintenance, GW_maintenance, Agri_maintenance, + ODK_agrohorticulture, +) +from moderation.utils.utils import ( + MODEL_FIELD_EXTRACTORS as _MODERATION_EXTRACTORS, + extract_lat_lon_from_gps, +) +from utilities.constants import ODK_URL_SESSION + +logger = logging.getLogger(__name__) + +_token_cache = { + "token": None, + "expires_at": None, +} + +# MARK: Helper +def normalize_name(name): + """ + Normalize names for comparison by: + - Converting to lowercase + - Replacing spaces with underscores + - Removing extra whitespace + """ + if not name: + return "" + return name.lower().replace(" ", "_").strip() + +_RESOURCE_TYPES_FLAT_HEADER = frozenset({ + "settlement", "well", "waterbody", "cropping", +}) +_RESOURCE_TYPES_UNION_HEADER = frozenset({ + "plan_gw", "plan_agri", "main_swb", "main_gw", "main_swb_rs", "main_agri", + "livelihood", "agrohorticulture", +}) + + +def _write_csv(resource_type, modified_response_list, all_keys, csv_path): + if resource_type in _RESOURCE_TYPES_FLAT_HEADER: + header_keys = modified_response_list[0].keys() + with open(csv_path, "w", encoding="utf-8") as output_file: + dict_writer = csv.DictWriter( + output_file, fieldnames=header_keys, extrasaction="ignore" + ) + dict_writer.writeheader() + dict_writer.writerows(modified_response_list) + elif resource_type in _RESOURCE_TYPES_UNION_HEADER: + with open(csv_path, "w", newline="", encoding="utf-8") as csvfile: + dict_writer = csv.DictWriter(csvfile, fieldnames=list(all_keys)) + dict_writer.writeheader() + for item in modified_response_list: + dict_writer.writerow(flatten_dict(item)) + logger.info(f"CSV generated for '{resource_type}' at {csv_path}") + + +# MARK: Modify ODK Settlement Data +def modify_response_list_settlement(res, block, plan_id): + res_list = [] + logger.info( + "modify_response_list_settlement: block=%s plan_id=%s input_records=%d", + block, plan_id, len(res) if res else 0, + ) + for result in res: + if result is None: + continue + + if result.get("__system", {}).get("reviewState") == "rejected": + continue + + try: + if normalize_name(result.get("block_name").lower()) != normalize_name(block): + continue + except AttributeError: + continue + + if str(result.get("plan_id")) != str(plan_id): + continue + + latitude, longitude = extract_lat_lon_from_gps(result.get("GPS_point")) + if latitude is not None and longitude is not None: + result["latitude"] = latitude + result["longitude"] = longitude + + result["status_re"] = result["__system"]["reviewState"] + result["sett_id"] = result["Settlements_id"] + result["sett_name"] = result["Settlements_name"] + try: + mgnrega_info = result.get("MNREGA_INFORMATION", {}) + except Exception: + logger.exception("modify_response_list_settlement: failed reading MNREGA_INFORMATION") + continue + if mgnrega_info: + result["job_aware"] = mgnrega_info.get("NREGA_aware", "") or 0 + result["job_applied"] = mgnrega_info.get("NREGA_applied", "") or 0 + result["job_card"] = mgnrega_info.get("NREGA_job_card", "") or 0 + result["without_jc"] = mgnrega_info.get("total_household", "") or 0 + result["work_days"] = mgnrega_info.get("NREGA_work_days", "") or 0 + result["past_work"] = mgnrega_info.get("q1", "") or "0" + result["raise_demand"] = mgnrega_info.get("select_one_Y_N", "") or "0" + result["demand"] = mgnrega_info.get("select_one_demands", "") or "0" + result["issues"] = mgnrega_info.get("select_multiple_issues", "") or "0" + result["community"] = ( + mgnrega_info.get("select_one_contributions", "") or "0" + ) + res_list.append(result) + return res_list + + +# MARK: Modify ODK Well Data +def modify_response_list_well(res, block, plan_id): + res_list = [] + for result in res: + if result is None: + continue + + if result.get("__system", {}).get("reviewState") == "rejected": + continue + + try: + if normalize_name(result.get("block_name").lower()) != normalize_name(block): + continue + except AttributeError: + continue + + if str(result.get("plan_id")) != str(plan_id): + continue + + latitude, longitude = extract_lat_lon_from_gps(result.get("GPS_point")) + if latitude is not None and longitude is not None: + result["latitude"] = latitude + result["longitude"] = longitude + + result["status_re"] = result["__system"]["reviewState"] + result["well_id"] = result["well_id"] + + well_usage_section = result.get("Well_usage", {}) + try: + result["ben_settlement"] = result.get("beneficiary_settlement", "") or "NA" + result["owner"] = result.get("select_one_owns", "") or "NA" + result["hh_benefitted"] = result.get("households_benefited", "") or "NA" + result["caste"] = result.get("select_multiple_caste_use", "") or "NA" + result["functional"] = ( + well_usage_section.get("select_one_Functional_Non_functional", "") + or "NA" + ) + result["need_maintenance"] = ( + well_usage_section.get("select_one_maintenance", "") or "NA" + ) + repair_value = well_usage_section.get("select_one_repairs_well") + if repair_value: + repair_value = str(repair_value).lower() + if repair_value == "other": + result["repair"] = ( + well_usage_section.get("select_one_repairs_well_other", "") + or "NA" + ) + else: + result["repair"] = repair_value + else: + result["repair"] = "NA" + except Exception: + logger.exception("modify_response_list_well: failed enriching record") + continue + res_list.append(result) + + return res_list + + +# MARK: Modify ODK Waterbody Data +def modify_response_list_waterbody(res, block, plan_id): + res_list = [] + logger.info( + "modify_response_list_waterbody: block=%s plan_id=%s input_records=%d", + block, plan_id, len(res) if res else 0, + ) + for result in res: + if result is None: + continue + if result.get("__system", {}).get("reviewState") == "rejected": + continue + try: + if normalize_name(result.get("block_name").lower()) != normalize_name(block): + continue + except AttributeError: + continue + if str(result.get("plan_id")) != str(plan_id): + continue + + latitude, longitude = extract_lat_lon_from_gps(result.get("GPS_point")) + if latitude is not None and longitude is not None: + result["latitude"] = latitude + result["longitude"] = longitude + + result["status_re"] = result["__system"]["reviewState"] + result["wb_id"] = result["waterbodies_id"] + + # type_of_water_st = result["select_one_water_structure"] + # if type_of_water_st: + # type_of_water_st = str(type_of_water_st).lower() + # if type_of_water_st == "other": + # result["wbs_type"] = result.get("select_one_water_structure_other", "") or "" + # else: + # result["wbs_type"] = result.get("select_one_water_structure", "") or "" + # else: + # result["wbs_type"] = "0" + + result["wbs_type"] = result.get("select_one_water_structure", "") or "0" + + try: + manager = result["select_one_manages"] + if manager: + manager = str(manager).lower() + if manager == "other": + result["who_manages"] = result.get("text_one_manages", "") or "" + else: + result["who_manages"] = result.get("select_one_manages", "") or "" + else: + result["who_manages"] = "0" + + who_owns = result["select_one_owns"] + if who_owns: + who_owns = str(who_owns).lower() + if who_owns == "other" or who_owns == "any other": + result["owner"] = result.get("text_one_owns", "") or "" + else: + result["owner"] = result.get("select_one_owns", "") or "" + else: + result["owner"] = "0" + result["caste"] = result.get("select_multiple_caste_use", "") or "0" + result["hh_benefitted"] = result.get("households_benefited", "") or 0 + result["identified"] = result.get("select_one_identified", "") or "0" + result["need_maintenance"] = result.get("select_one_maintenance") or "0" + + # Handle the dynamic water structure dimensions + # water_structure_type = result.get("select_one_water_structure", "").lower().replace("_", " ") + # water_structure_dimension = {} + # for key, value in result.items(): + # if isinstance(value, dict): + # structure_type = key.lower().replace("_", " ") + # if structure_type == water_structure_type: + # water_structure_dimension = { + # "length": next((v for k, v in value.items() if k.startswith("Length")), None), + # "breadth": next((v for k, v in value.items() if k.startswith("Breadth")), None), + # "width": next((v for k, v in value.items() if k.startswith("Width")), None), + # "depth": next((v for k, v in value.items() if k.startswith("Depth")), None), + # "height": next((v for k, v in value.items() if k.startswith("Height")), None), + # } + # break + + # Add the dimensions to the result dictionary + # result.update(water_structure_dimension) + except Exception: + logger.exception("modify_response_list_waterbody: failed enriching record") + continue + res_list.append(result) + return res_list + + +# MARK: Modify ODK Cropping Data +def modify_reponse_list_cropping(res, block, plan_id): + res_list = [] + logger.info( + "modify_reponse_list_cropping: block=%s plan_id=%s input_records=%d", + block, plan_id, len(res) if res else 0, + ) + + for result in res: + if result is None: + continue + + if result.get("__system", {}).get("reviewState") == "rejected": + continue + + try: + if normalize_name(result.get("block_name").lower()) != normalize_name(block): + continue + except AttributeError: + continue + + + if str(result.get("plan_id")) != str(plan_id): + continue + + + latitude, longitude = extract_lat_lon_from_gps(result.get("GPS_point")) + if latitude is not None and longitude is not None: + result["latitude"] = latitude + result["longitude"] = longitude + + result["status_re"] = result["__system"]["reviewState"] + result["crop_id"] = result["__id"] + + # Settlement and land basic information (5 keys) + result["sett_name"] = result.get("beneficiary_settlement", "") or "" + result["uncropp_br"] = result.get("Uncropped_barren_land", "") or "" + result["irrigatn"] = result.get("select_multiple_widgets", "") or "" + result["land_cls"] = result.get("select_one_classified", "") or "" + result["crop_seas"] = result.get("select_one_practice", "") or "" + + # Kharif season information (3 keys) + result["crop_khrf"] = result.get("select_multiple_cropping_kharif", "") or "" + result["crop_kh_o"] = result.get("select_multiple_cropping_kharif_other", "") or "" + result["area_khrf"] = result.get("total_area_cultivation_kharif", "") or "" + + # Rabi season information (3 keys) + result["crops_rabi"] = result.get("select_multiple_cropping_Rabi", "") or "" + result["crop_rb_o"] = result.get("select_multiple_cropping_Rabi_other", "") or "" + result["area_rabi"] = result.get("total_area_cultivation_Rabi", "") or "" + + # Zaid season information (3 keys) + result["crops_zaid"] = result.get("select_multiple_cropping_Zaid", "") or "" + result["crop_zd_o"] = result.get("select_multiple_cropping_Zaid_other", "") or "" + result["area_zaid"] = result.get("total_area_cultivation_Zaid", "") or "" + + # Soil and productivity information (4 keys) + result["productiv"] = result.get("select_one_productivity", "") or "" + result["soil_deg"] = result.get("soil_degraded", "") or "" + result["deg_reas"] = result.get("select_one_reason_degradation", "") or "" + result["deg_reas2"] = result.get("select_one_reason_degradation_1", "") or "" + + res_list.append(result) + + return res_list + + +# MARK: Modify ODK Plan Data +def modify_response_list_plan(res, block, plan_id): + res_list = [] + for result in res: + if result is None: + continue + + if result.get("__system", {}).get("reviewState") == "rejected": + continue + + try: + if normalize_name(result.get("block_name").lower()) != normalize_name(block): + continue + except AttributeError: + continue + + if str(result.get("plan_id")) != str(plan_id): + continue + + latitude, longitude = extract_lat_lon_from_gps(result.get("GPS_point")) + if latitude is not None and longitude is not None: + result["latitude"] = latitude + result["longitude"] = longitude + + result["status_re"] = result["__system"]["reviewState"] + result["work_id"] = result["work_id"] + + work_type = None + selected_work = None + + if "TYPE_OF_WORK" in result: + work_type = result["TYPE_OF_WORK"] + elif "TYPE_OF_WORK_ID" in result: + work_type = result["TYPE_OF_WORK_ID"] + + if work_type: + result["work_type"] = work_type + + work_type_key = re.sub(r"[^a-zA-Z0-9]+", "_", work_type) + + if work_type_key in result: + selected_work = result[work_type_key] + if selected_work: + result["selected_work"] = selected_work + else: + result["selected_work"] = work_type_key + elif work_type in result: + selected_work = result[work_type] + if selected_work: + result["selected_work"] = selected_work + else: + result["selected_work"] = work_type + else: + result["selected_work"] = work_type + + result["ben_settlement"] = result["beneficiary_settlement"] + result["ben_name"] = result["Beneficiary_Name"] + result["ben_contact"] = result["Beneficiary_Contact_Number"] + res_list.append(result) + + return res_list + + +# MARK: Modify ODK Livelihood Data +def modify_response_list_livelihood(res, block, plan_id): + res_list = [] + for result in res: + # if result["__system"]["reviewState"] != "rejected": + if result is None: + continue + + if result.get("__system", {}).get("reviewState") == "rejected": + continue + + try: + if normalize_name(result.get("block_name").lower()) != normalize_name(block): + continue + except AttributeError: + continue + + if str(result.get("plan_id")) != str(plan_id): + continue + + latitude, longitude = extract_lat_lon_from_gps(result.get("GPS_point")) + if latitude is not None and longitude is not None: + result["latitude"] = latitude + result["longitude"] = longitude + + result["status_re"] = result["__system"]["reviewState"] + res_list.append(result) + return res_list + + +# MARK: Modify ODK Maintenance / Agrohorticulture (Generic) +def modify_response_list_work(res, block, plan_id): + """ + Robust transform for maintenance and agrohorticulture submissions. + Unlike `modify_response_list_plan`, this avoids bracket-access on keys + that maintenance/agrohorticulture blobs may not carry (e.g. work_id, + Beneficiary_Name). Block filter is best-effort: applied only when the + blob actually has block_name (these models have no block_name DB column). + """ + res_list = [] + for result in res: + if result is None: + continue + if not isinstance(result, dict): + continue + if result.get("__system", {}).get("reviewState") == "rejected": + continue + + blob_block = result.get("block_name") + if blob_block: + try: + if normalize_name(str(blob_block).lower()) != normalize_name(block): + continue + except AttributeError: + continue + + if str(result.get("plan_id")) != str(plan_id): + continue + + lat, lon = extract_lat_lon_from_gps(result.get("GPS_point")) + if lat is not None and lon is not None: + result["latitude"] = lat + result["longitude"] = lon + + sys_info = result.get("__system") or {} + if isinstance(sys_info, dict): + review_state = sys_info.get("reviewState") + if review_state: + result["status_re"] = review_state + + res_list.append(result) + return res_list + + +# Layer-build source-of-truth registry. Key = resource_type / work_type the +# `/add_resources` and `/add_works` endpoints accept. Tuple = (Model, JSON +# blob field, model has block_name column for DB-level pre-filtering). +# +# Resources (workspace=resources): settlement, well, waterbody, cropping +# Works (workspace=works): +# plan_gw — new recharge structures (groundwater) +# main_gw — maintenance of recharge structures +# plan_agri — new irrigation structures +# main_agri — maintenance of irrigation structures +# main_swb — surface water body maintenance (water-structure form) +# main_swb_rs — remote-sensed surface water body maintenance +# livelihood — livelihood +# agrohorticulture — agrohorticulture +_DB_CONFIG = { + "settlement": (ODK_settlement, "data_settlement", True), + "well": (ODK_well, "data_well", True), + "waterbody": (ODK_waterbody, "data_waterbody", True), + "cropping": (ODK_crop, "data_crop", False), + "plan_gw": (ODK_groundwater, "data_groundwater", True), + "plan_agri": (ODK_agri, "data_agri", True), + "livelihood": (ODK_livelihood, "data_livelihood", True), + "main_swb": (SWB_maintenance, "data_swb_maintenance", False), + "main_gw": (GW_maintenance, "data_gw_maintenance", False), + "main_swb_rs": (SWB_RS_maintenance, "data_swb_rs_maintenance", False), + "main_agri": (Agri_maintenance, "data_agri_maintenance", False), + # `data_agohorticulture` is the actual model field name — there is a + # spelling typo in the schema. Respecting it here to avoid a migration. + "agrohorticulture": (ODK_agrohorticulture, "data_agohorticulture", False), +} + +# Fields never useful to project alongside the JSON blob: the blob itself, +# soft-delete metadata, and relational fields (FKs serialise poorly via values()). +_PROJECTION_EXCLUDED_FIELDS = frozenset({ + "data_before_moderation", + "is_deleted", + "deleted_at", + "deleted_by", + "moderated_by", +}) + + +def _scalar_projection_fields(model, json_blob_field: str) -> list: + """ + Concrete, non-relational fields on `model` safe to project via `.values()` + alongside the raw ODK JSON blob. Captures everything moderation can edit + (settlement_name, block_name, nrega_*, lat/lon, status_re, ...) so the + generated layer reflects the latest moderated values, not just the + original ODK submission stored in `data_`. + """ + skip = {json_blob_field, *_PROJECTION_EXCLUDED_FIELDS} + fields = [] + for f in model._meta.get_fields(): + if not getattr(f, "concrete", False): + continue + if f.many_to_one or f.one_to_one or f.many_to_many or f.one_to_many: + continue + if f.name in skip: + continue + fields.append(f.name) + return fields + + +def _merge_moderated(blob: dict, friendly: dict, friendly_canonical: bool) -> dict: + """ + Merge friendly DB column values into the raw ODK blob so the layer carries + both the original ODK keys (Settlements_name, GPS_point, ...) and the + moderated friendly columns (settlement_name, block_name, ...). + + `friendly_canonical=True` (model has a moderation extractor): friendly + columns are kept in sync with every moderation edit, so they win on + collision. `False` (no extractor, e.g. SWB_maintenance): moderation only + touches the blob, so the blob wins on collision. + + GeoPackage/SQLite (the downstream layer format) is case-insensitive on + column names, so a blob key like "GPS_point" and a friendly column + "gps_point" would collide on write. We dedupe case-insensitively in + favour of the canonical side; non-colliding keys (e.g. "Settlements_name" + vs "settlement_name") are both preserved. + """ + if friendly_canonical: + winner, loser = friendly, blob + else: + winner, loser = blob, friendly + + winner_lower = {k.lower() for k in winner} + loser_filtered = { + k: v for k, v in loser.items() + if k.lower() not in winner_lower + } + return {**loser_filtered, **winner} + + +# MARK: Fetch DB Data +def fetch_db_data(csv_path, resource_type, block, plan_id) -> int: + """ + Build the CSV of records for the given (resource_type, plan_id, block) + by reading from our DB (post-moderation source of truth). + + Returns the number of rows actually written to the CSV; 0 means no + usable data was found and the caller should treat it as a soft 404. + """ + logger.info( + f"fetch_db_data: starting — resource_type={resource_type}, " + f"plan_id={plan_id}, block={block}, csv_path={csv_path}" + ) + + entry = _DB_CONFIG.get(resource_type) + if not entry: + logger.warning(f"fetch_db_data: unknown resource_type '{resource_type}'") + return 0 + + model, data_field, has_block_col = entry + projection_fields = _scalar_projection_fields(model, data_field) + friendly_canonical = model in _MODERATION_EXTRACTORS + logger.info( + f"fetch_db_data: querying {model.__name__}.{data_field} " + f"with plan_id={plan_id}, is_deleted=False" + + ( + f", block_name icontains '{block}'" + if has_block_col + else " (no block_name column, skipping DB block filter)" + ) + ) + logger.info( + f"fetch_db_data: projecting blob '{data_field}' + " + f"{len(projection_fields)} moderated column(s) " + f"(friendly_canonical={friendly_canonical}): {projection_fields}" + ) + + qs = model.objects.filter(plan_id=str(plan_id), is_deleted=False) + if has_block_col: + qs = qs.filter(block_name__icontains=block.replace("_", " ").strip()) + + raw_rows = list(qs.values(data_field, *projection_fields)) + logger.info( + f"fetch_db_data: DB returned {len(raw_rows)} record(s) for " + f"resource_type={resource_type}, plan_id={plan_id}" + ) + + response_list = [] + empty_blob_count = 0 + for row in raw_rows: + blob = row.get(data_field) or {} + if not blob: + empty_blob_count += 1 + continue + friendly = {k: v for k, v in row.items() if k != data_field} + response_list.append(_merge_moderated(blob, friendly, friendly_canonical)) + + if empty_blob_count: + logger.warning( + f"fetch_db_data: skipped {empty_blob_count} record(s) with empty " + f"{data_field}" + ) + + if not response_list: + logger.warning( + f"fetch_db_data: no usable records for resource_type={resource_type}, " + f"plan_id={plan_id}, block={block}" + ) + return 0 + + logger.info( + f"fetch_db_data: running transform for resource_type={resource_type} " + f"on {len(response_list)} record(s) (each enriched with " + f"{len(projection_fields)} moderated column(s))" + ) + + all_keys = set() + if resource_type == "settlement": + rows = modify_response_list_settlement(response_list, block, plan_id) + elif resource_type == "well": + rows = modify_response_list_well(response_list, block, plan_id) + elif resource_type == "waterbody": + rows = modify_response_list_waterbody(response_list, block, plan_id) + elif resource_type == "cropping": + rows = modify_reponse_list_cropping(response_list, block, plan_id) + elif resource_type in ["plan_gw", "main_swb", "plan_agri"]: + rows = modify_response_list_plan(response_list, block, plan_id) + for item in rows: + all_keys.update(extract_keys(item)) + elif resource_type == "livelihood": + rows = modify_response_list_livelihood(response_list, block, plan_id) + for item in rows: + all_keys.update(extract_keys(item)) + elif resource_type in [ + "main_gw", "main_swb_rs", "main_agri", "agrohorticulture", + ]: + rows = modify_response_list_work(response_list, block, plan_id) + for item in rows: + all_keys.update(extract_keys(item)) + else: + logger.warning( + f"fetch_db_data: no transform defined for resource_type='{resource_type}'" + ) + return 0 + + logger.info( + f"fetch_db_data: transform produced {len(rows)} row(s) " + f"(filtered from {len(response_list)}) for resource_type={resource_type}" + ) + + if not rows: + logger.warning( + f"fetch_db_data: transform returned empty list for " + f"resource_type={resource_type}, plan_id={plan_id}, block={block}" + ) + return 0 + + logger.info(f"fetch_db_data: writing CSV to {csv_path}") + _write_csv(resource_type, rows, all_keys, csv_path) + logger.info( + f"fetch_db_data: done — {len(rows)} row(s) written to {csv_path} " + f"(columns include {len(projection_fields)} moderated friendly field(s))" + ) + return len(rows) + + +def flatten_dict(d, parent_key="", sep="_"): + items = [] + for k, v in d.items(): + new_key = f"{parent_key}{sep}{k}" if parent_key else k + if isinstance(v, dict): + items.extend(flatten_dict(v, new_key, sep=sep).items()) + else: + items.append((new_key, v)) + return dict(items) + + +def extract_keys(d, parent_key="", sep="_"): + keys = [] + for k, v in d.items(): + new_key = f"{parent_key}{sep}{k}" if parent_key else k + keys.append(new_key) + if isinstance(v, dict): + keys.extend(extract_keys(v, new_key, sep=sep)) + return keys + + +# MARK: Bearer Token +def fetch_bearer_token(email: str, password: str) -> str: + try: + if _token_cache["token"] and _token_cache["expires_at"]: + now = datetime.now(timezone.utc) + if now < _token_cache["expires_at"]: + return _token_cache["token"] + + response = requests.post( + ODK_URL_SESSION, json={"email": email, "password": password} + ) + print("Response: ", response) + if response.status_code == 200: + response_data = response.json() + _token_cache["token"] = response_data.get("token") + _token_cache["expires_at"] = dateutil.parser.parse( + response_data.get("expiresAt") + ) + return _token_cache["token"] + else: + raise Exception( + f"Failed to fetch bearer token. Status code: {response.status_code}" + ) + except Exception as e: + print(f"An error occurred while fetching the bearer token: {str(e)}") + raise diff --git a/stats_generator/mws_indicators.py b/stats_generator/mws_indicators.py index bf8f311b..e67d272c 100644 --- a/stats_generator/mws_indicators.py +++ b/stats_generator/mws_indicators.py @@ -1,1119 +1,1130 @@ -import os -import requests, json -import pandas as pd -import pymannkendall as mk -import numpy as np -import ast -from nrm_app.settings import EXCEL_PATH -from .utils import get_url -from rest_framework.response import Response -from rest_framework import status -from django.http import HttpResponse -from .models import LayerInfo - - -def create_geojson_for_all_mws(existing_geojson_path, df, new_geojson_path): - with open(existing_geojson_path) as f: - existing_data = json.load(f) - - features = [] - - for _, row in df.iterrows(): - uid = row["mws_id"] - geometry = None - - for feature in existing_data["features"]: - if feature["properties"].get("uid") == uid: - geometry = feature["geometry"] - break - - if geometry is None: - print( - f"No geometry found for uid: {uid}. Using default geometry (e.g., None)." - ) - geometry = {"type": "Point", "coordinates": [0, 0]} - - properties = row.to_dict() - - new_feature = { - "type": "Feature", - "geometry": geometry, - "properties": properties, - } - features.append(new_feature) - - new_feature_collection = {"type": "FeatureCollection", "features": features} - - with open(new_geojson_path, "w") as f: - json.dump(new_feature_collection, f) - - -def generate_mws_data_for_kyl_filters( - state, district, block, file_type, regenerate=None -): - state_folder = state.replace(" ", "_").upper() - district_folder = district.replace(" ", "_").upper() - file_xl_path = os.path.join( - EXCEL_PATH, - "data/stats_excel_files", - state_folder, - district_folder, - f"{district}_{block}", - ) - if regenerate: - file_path = None - else: - file_path = get_mws_KYL_filter_data(state, district, block, file_type) - if not file_path: - try: - sheets = { - "hydrological_annual": -1, - "terrain": -1, - "croppingIntensity_annual": -1, - "surfaceWaterBodies_annual": -1, - "croppingDrought_kharif": -1, - "nrega_annual": -1, - "mws_intersect_villages": -1, - "change_detection_degradation": -1, - "change_detection_afforestation": -1, - "change_detection_deforestation": -1, - "change_detection_urbanization": -1, - "change_detection_cropintensity": -1, - "terrain_lulc_slope": -1, - "terrain_lulc_plain": -1, - "restoration_vector": -1, - "aquifer_vector": -1, - "soge_vector": -1, - "lcw_conflict": -1, - "factory_csr": -1, - "mining": -1, - "green_credit": -1, - "mws_intersect_swb": -1, - "dem": -1, - "canal": -1, - "river": -1, - "lulc_vector": -1, - "drainage_density": -1, - } - - try: - with pd.ExcelFile(file_xl_path + ".xlsx") as xl: - available_sheets = xl.sheet_names # Get list of available sheets - - # Try to parse each sheet if it exists - for sheet_name in sheets.keys(): - if sheet_name in available_sheets: - try: - sheets[sheet_name] = xl.parse(sheet_name) - except Exception as e: - print(f"Error parsing sheet {sheet_name}: {e}") - sheets[sheet_name] = -1 - else: - print(f"Sheet {sheet_name} not found in Excel file") - sheets[sheet_name] = -1 - - except Exception as e: - print(f"Error reading Excel file: {e}") - # Return all sheets as -1 if the file can't be read - return {k: -1 for k in sheets.keys()} - - results = [] - df_hydrological_annual = sheets["hydrological_annual"] - - for specific_mws_id in df_hydrological_annual["UID"].unique(): - hydro_annual_mws_data = df_hydrological_annual[ - df_hydrological_annual["UID"] == specific_mws_id - ] - precipitation_columns = hydro_annual_mws_data.filter( - like="Precipitation" - ) # Avg_precipitation - total_percipitation_column = precipitation_columns.shape[1] - sum_precipitation = precipitation_columns.sum(axis=1).sum() - avg_percipitation = round( - sum_precipitation / total_percipitation_column, 4 - ) - - try: - terrain_vector_mws_data = sheets["terrain"][ - sheets["terrain"]["UID"] == specific_mws_id - ] - terrainCluster_ID = terrain_vector_mws_data.get( - "terrain_cluster_id", None - ).iloc[ - 0 - ] # terrain - except: - terrainCluster_ID = "" - - try: - df_crp_intensity_mws_data = sheets["croppingIntensity_annual"][ - sheets["croppingIntensity_annual"]["UID"] == specific_mws_id - ] - df_crp_intensity_mws_data = df_crp_intensity_mws_data.fillna(0) - - crp_Intensity_columns = df_crp_intensity_mws_data.filter( - like="cropping_intensity_unit_less" - ) # cropping_intensity_avg - total_crp_Intensity_column = crp_Intensity_columns.shape[1] - sum_crp_Intensity = crp_Intensity_columns.sum(axis=1).sum() - cropping_intensity_avg = round( - ( - sum_crp_Intensity / total_crp_Intensity_column - if total_crp_Intensity_column > 0 - else 0 - ), - 4, - ) - - ######### Cropping Intensity Trend ################# - crp_intensity_T = df_crp_intensity_mws_data.filter( - like="cropping_intensity_unit_less" - ).dropna() # Drop rows with NaN for trend calculation - crp_intensity_T = crp_intensity_T.squeeze().tolist()[:-3] - result = mk.original_test(crp_intensity_T) - - def sens_slope(data): - slopes = [] - for i in range(len(data) - 1): - for j in range(i + 1, len(data)): - s = (data[j] - data[i]) / (j - i) - slopes.append(s) - return np.median(slopes) - - cropping_intensity_trend_value = sens_slope(crp_intensity_T) - cropping_intensity_trend = None - if result.trend == "no trend": - cropping_intensity_trend = "0" - elif result.trend == "increasing": - cropping_intensity_trend = "1" - else: - cropping_intensity_trend = "-1" - - # Total cropped area, replace NaN with 0 for these columns as well - total_cropped_area = df_crp_intensity_mws_data.iloc[0][ - "sum_area_in_ha" - ] - - # Handle single-cropped area calculation - single_crop_columns = df_crp_intensity_mws_data.filter( - like="single_cropped_area" - ) # avg_single_cropped - total_single_crop_column = single_crop_columns.shape[1] - sum_single_crop = single_crop_columns.sum(axis=1).sum() - percent_single_crop = ( - sum_single_crop * 100 / total_cropped_area - if total_cropped_area > 0 - else 0 - ) - avg_single_cropped = round( - ( - percent_single_crop / total_single_crop_column - if total_single_crop_column > 0 - else 0 - ), - 4, - ) - - # Handle doubly-cropped area calculation - double_crop_columns = df_crp_intensity_mws_data.filter( - like="doubly_cropped_area" - ) # avg_double_cropped - total_double_crop_column = double_crop_columns.shape[1] - sum_double_crop = double_crop_columns.sum(axis=1).sum() - percent_double_crop = ( - sum_double_crop * 100 / total_cropped_area - if total_cropped_area > 0 - else 0 - ) - avg_double_cropped = round( - ( - percent_double_crop / total_double_crop_column - if total_double_crop_column > 0 - else 0 - ), - 4, - ) - - # Handle triply-cropped area calculation - triply_crop_columns = df_crp_intensity_mws_data.filter( - like="triply_cropped_area" - ) # avg_triply_cropped - total_triply_crop_column = triply_crop_columns.shape[1] - sum_triply_crop = triply_crop_columns.sum(axis=1).sum() - percent_triply_crop = ( - sum_triply_crop * 100 / total_cropped_area - if total_cropped_area > 0 - else 0 - ) - avg_triply_cropped = round( - ( - percent_triply_crop / total_triply_crop_column - if total_triply_crop_column > 0 - else 0 - ), - 4, - ) - - except Exception as e: - # Handle exception and ensure all variables are set - cropping_intensity_avg = 0 - cropping_intensity_trend = "" - avg_single_cropped = 0 - avg_double_cropped = 0 - avg_triply_cropped = 0 - print(f"Error occurred: {e}") - - try: - df_swb_annual_mws_data = sheets["surfaceWaterBodies_annual"][ - sheets["surfaceWaterBodies_annual"]["UID"] == specific_mws_id - ] - df_crp_intensity_mws_data = sheets["croppingIntensity_annual"][ - sheets["croppingIntensity_annual"]["UID"] == specific_mws_id - ] - - df_crp_intensity_mws_data = df_crp_intensity_mws_data.fillna(0) - df_swb_annual_mws_data = df_swb_annual_mws_data.fillna(0) - swb_area_kharif_columns = df_swb_annual_mws_data.filter( - like="kharif_area" - ) - single_kharif_crop_columns = df_crp_intensity_mws_data.filter( - like="single_kharif_cropped_area" - ) - double_crop_columns = df_crp_intensity_mws_data.filter( - like="doubly_cropped_area" - ) - triply_crop_columns = df_crp_intensity_mws_data.filter( - like="triply_cropped_area" - ) - - combined_columns_kharif = single_kharif_crop_columns.add( - double_crop_columns, fill_value=0 - ) - combined_columns_kharif = combined_columns_kharif.add( - triply_crop_columns, fill_value=0 - ) - total_cropped_area_kharif = combined_columns_kharif.sum( - axis=1 - ).sum() - total_swb_area_kharif_column = swb_area_kharif_columns.shape[1] - sum_swb_area_kharif = swb_area_kharif_columns.sum(axis=1).sum() - - avg_wsr_ratio_kharif = ( - sum_swb_area_kharif / total_cropped_area_kharif - if total_cropped_area_kharif > 0 - else 0 - ) - avg_wsr_ratio_kharif = round( - avg_wsr_ratio_kharif * 100 / total_swb_area_kharif_column, 4 - ) - swb_area_rabi_columns = df_swb_annual_mws_data.filter( - like="rabi_area" - ) - single_non_kharif_crop_columns = df_crp_intensity_mws_data.filter( - like="single_non_kharif_cropped_area" - ) - - # Combine the cropping areas and calculate total cropped area for Rabi - combined_columns_rabi = single_non_kharif_crop_columns.add( - double_crop_columns, fill_value=0 - ) - combined_columns_rabi = combined_columns_rabi.add( - triply_crop_columns, fill_value=0 - ) - total_cropped_area_rabi = combined_columns_rabi.sum(axis=1).sum() - - total_swb_rabi_column = swb_area_rabi_columns.shape[1] - sum_swb_area_rabi = swb_area_rabi_columns.sum(axis=1).sum() - - # Average WSR ratio for Rabi - avg_wsr_ratio_rabi = ( - sum_swb_area_rabi / total_cropped_area_rabi - if total_cropped_area_rabi > 0 - else 0 - ) - avg_wsr_ratio_rabi = round( - avg_wsr_ratio_rabi * 100 / total_swb_rabi_column, 4 - ) - swb_area_zaid_columns = df_swb_annual_mws_data.filter( - like="zaid_area" - ) - total_cropped_area_zaid = triply_crop_columns.sum(axis=1).sum() - - total_swb_zaid_column = swb_area_zaid_columns.shape[1] - sum_swb_area_zaid = swb_area_zaid_columns.sum(axis=1).sum() - avg_wsr_ratio_zaid = ( - sum_swb_area_zaid / total_cropped_area_zaid - if total_cropped_area_zaid > 0 - else 0 - ) - avg_wsr_ratio_zaid = round( - avg_wsr_ratio_zaid * 100 / total_swb_zaid_column, 4 - ) - - except Exception as e: - avg_wsr_ratio_kharif = 0 - avg_wsr_ratio_rabi = 0 - avg_wsr_ratio_zaid = 0 - print(f"Error occurred: {e}") - - ############ Swb_average - avg_kharif_surface_water_mws = 0 - avg_rabi_surface_water_mws = 0 - avg_zaid_surface_water_mws = 0 - df_swb_annual_mws_data = sheets["surfaceWaterBodies_annual"][ - sheets["surfaceWaterBodies_annual"]["UID"] == specific_mws_id - ] - if not df_swb_annual_mws_data.empty: - total_swb_area = df_swb_annual_mws_data.iloc[0][ - "total_swb_area_in_ha" - ] - - if total_swb_area != 0: # Check if total_swb_area is not zero - swb_area_kharif_columns = df_swb_annual_mws_data.filter( - like="kharif_area" - ) - total_swb_area_kharif_column = swb_area_kharif_columns.shape[1] - sum_swb_area_kharif = ( - swb_area_kharif_columns.sum(axis=1).sum() / total_swb_area - ) - avg_kharif_surface_water_mws = round( - ( - sum_swb_area_kharif * 100 / total_swb_area_kharif_column - if total_swb_area_kharif_column > 0 - else 0 - ), - 4, - ) - - swb_rabi_area_columns = df_swb_annual_mws_data.filter( - like="rabi_area" - ) - total_swb_rabi_area_column = swb_rabi_area_columns.shape[1] - sum_swb_rabi_area = ( - swb_rabi_area_columns.sum(axis=1).sum() / total_swb_area - ) - avg_rabi_surface_water_mws = round( - ( - sum_swb_rabi_area * 100 / total_swb_rabi_area_column - if total_swb_rabi_area_column > 0 - else 0 - ), - 4, - ) - - swb_zaid_area_columns = df_swb_annual_mws_data.filter( - like="zaid_area" - ) - total_swb_zaid_area_column = swb_zaid_area_columns.shape[1] - sum_swb_zaid_area = ( - swb_zaid_area_columns.sum(axis=1).sum() / total_swb_area - ) - avg_zaid_surface_water_mws = round( - ( - sum_swb_zaid_area * 100 / total_swb_zaid_area_column - if total_swb_zaid_area_column > 0 - else 0 - ), - 4, - ) - else: - avg_perc_kharif_surface_water_mws = ( - avg_perc_rabi_surface_water_mws - ) = avg_perc_zaid_surface_water_mws = 0 - else: - print("DataFrame is empty. No data to process.") - avg_perc_kharif_surface_water_mws = ( - avg_perc_rabi_surface_water_mws - ) = avg_perc_zaid_surface_water_mws = 0 - - ################# SWB Trend ###################### - try: - df_swb_annual_mws_data = sheets["surfaceWaterBodies_annual"][ - sheets["surfaceWaterBodies_annual"]["UID"] == specific_mws_id - ] - swb_T = df_swb_annual_mws_data.filter( - like="total_area_in_ha" - ).dropna() # Drop rows with NaN for trend calculation - swb_T = swb_T.iloc[0].dropna().tolist() - result = mk.original_test(swb_T) - - trend_swb = None - if result.trend == "no trend": - trend_swb = "0" - elif result.trend == "increasing": - trend_swb = "1" - else: - trend_swb = "-1" - except: - trend_swb = "-1" - - ######### G Trend ################# - G_Trend = ( - hydro_annual_mws_data.filter(like="G") - .drop(columns=hydro_annual_mws_data.filter(like="DeltaG").columns) - .dropna() - ) - G_Trend = G_Trend.squeeze().tolist() - result = mk.original_test(G_Trend) - - def sens_slope(data): - slopes = [] - for i in range(len(data) - 1): - for j in range(i + 1, len(data)): - s = (data[j] - data[i]) / (j - i) - slopes.append(s) - return np.median(slopes) - - trend_g_value = sens_slope(G_Trend) - trend_g = None - if result.trend == "no trend": - trend_g = "0" - elif result.trend == "increasing": - trend_g = "1" - else: - trend_g = "-1" - - ######### drought_category ############## - try: - - layers = LayerInfo.objects.get( - layer_type="vector", workspace="drought" - ) - years = [ - str(year) - for year in range(layers.start_year, layers.end_year + 1) - ] - - df_crpDrought_mws_data = sheets["croppingDrought_kharif"][ - sheets["croppingDrought_kharif"]["UID"] == specific_mws_id - ] - - sum_moderate_severe = { - year: ( - 1 - if ( - df_crpDrought_mws_data.iloc[0][ - f"Moderate_in_weeks_{year}" - ] - + df_crpDrought_mws_data.iloc[0][ - f"Severe_in_weeks_{year}" - ] - ) - >= 5 - else 0 - ) - for year in years - } - sum_of_values = sum(sum_moderate_severe.values()) - drought_category = None - if sum_of_values >= 2: - drought_category = 2 - else: - drought_category = sum_of_values - - ######## avg_dry_spell_in_weeks - dryspell_columns = df_crpDrought_mws_data.filter( - like="drysp_unit_4_weeks" - ) # avg_dry_spell_in_weeks - total_dryspell_column = dryspell_columns.shape[1] - sum_dryspell = dryspell_columns.sum(axis=1).sum() - avg_dry_spell_in_weeks = round( - ( - sum_dryspell / total_dryspell_column - if total_dryspell_column > 0 - else 0 - ), - 4, - ) - except: - drought_category = 0 - avg_dry_spell_in_weeks = 0 - - ################# avg_runoff - runoff_columns = hydro_annual_mws_data.filter( - like="RunOff" - ) # avg_runoff - total_runoff_column = runoff_columns.shape[1] - sum_runoff = runoff_columns.sum(axis=1).sum() - avg_runoff = sum_runoff / total_runoff_column - - ############## Nrega Asset ########################## - try: - df_nrega_assets_mws_data = sheets["nrega_annual"][ - sheets["nrega_annual"]["mws_id"] == specific_mws_id - ] - nrega_assets_sum = ( - df_nrega_assets_mws_data.iloc[:, 1:] - .select_dtypes(include="number") - .sum() - .sum() - ) - except: - nrega_assets_sum = 0 - - ############ MWS Intersect Villages ######################## - try: - df_mws_inters_villages_mws_data = sheets["mws_intersect_villages"][ - sheets["mws_intersect_villages"]["MWS UID"] == specific_mws_id - ] - mws_intersect_villages = df_mws_inters_villages_mws_data.get( - "Village IDs", None - ).iloc[0] - mws_intersect_villages = ast.literal_eval(mws_intersect_villages) - except: - mws_intersect_villages = [] - - ############ Change Detection Degradation ################### - try: - df_change_degr_detection_mws_data = sheets[ - "change_detection_degradation" - ][sheets["change_detection_degradation"]["UID"] == specific_mws_id] - degr_sum = ( - df_change_degr_detection_mws_data[ - [ - "farm_to_barren_area_in_ha", - "farm_to_scrub_land_area_in_ha", - ] - ] - .sum(axis=1) - .iloc[0] - ) - df_change_crp_detection_mws_data = sheets[ - "change_detection_cropintensity" - ][ - sheets["change_detection_cropintensity"]["UID"] - == specific_mws_id - ] - crp_sum = ( - df_change_crp_detection_mws_data[ - [ - "double_to_single_area_in_ha", - "triple_to_double_area_in_ha", - "triple_to_single_area_in_ha", - ] - ] - .sum(axis=1) - .iloc[0] - ) - degradation_land_area = degr_sum + crp_sum - change_in_cropping_intensity_area = ( - df_change_crp_detection_mws_data.get( - "total_change_crop_intensity_area_in_ha", None - ).iloc[0] - ) - - except: - degradation_land_area = 0 - change_in_cropping_intensity_area = 0 - - ############ Change Detection Afforestation ################### - try: - df_change_affo_detection_mws_data = sheets[ - "change_detection_afforestation" - ][ - sheets["change_detection_afforestation"]["UID"] - == specific_mws_id - ] - afforestation_column = [ - "barren_to_forest_area_in_ha", - "farm_to_forest_area_in_ha", - ] - afforestation_land_area = df_change_affo_detection_mws_data.get( - "total_afforestation_area_in_ha", None - ).iloc[0] - except: - afforestation_land_area = 0 - - ############ Change Detection Deforestation ################### - try: - df_change_defo_detection_mws_data = sheets[ - "change_detection_deforestation" - ][ - sheets["change_detection_deforestation"]["UID"] - == specific_mws_id - ] - deforestation_land_area = df_change_defo_detection_mws_data.get( - "total_deforestation_area_in_ha", None - ).iloc[0] - except: - deforestation_land_area = 0 - - ############ Change Detection Urbanization ################### - try: - df_change_urba_detection_mws_data = sheets[ - "change_detection_urbanization" - ][sheets["change_detection_urbanization"]["UID"] == specific_mws_id] - urbanization_land_area = df_change_urba_detection_mws_data.get( - "total_urbanization_area_in_ha", None - ).iloc[0] - except: - urbanization_land_area = 0 - - ############# Terrain lulc slope / plain ##################### - try: - df_lulc_slope_mws_data = sheets["terrain_lulc_slope"][ - sheets["terrain_lulc_slope"]["UID"] == specific_mws_id - ] - lulc_slope_category = ( - df_lulc_slope_mws_data.get("cluster_name", pd.NA).iloc[0] - if not df_lulc_slope_mws_data.empty - else None - ) - - except: - lulc_slope_category = "" - - try: - df_lulc_plain_mws_data = sheets["terrain_lulc_plain"][ - sheets["terrain_lulc_plain"]["UID"] == specific_mws_id - ] - lulc_plain_category = ( - df_lulc_plain_mws_data.get("cluster_name", pd.NA).iloc[0] - if not df_lulc_plain_mws_data.empty - else None - ) - - except: - lulc_plain_category = "" - - ################# Restoration Vector ######################### - try: - df_restoration_vector_mws_data = sheets["restoration_vector"][ - sheets["restoration_vector"]["UID"] == specific_mws_id - ] - wide_scale_restoration = df_restoration_vector_mws_data.get( - "wide_scale_restoration_area_in_ha", None - ).iloc[0] - area_protection = df_restoration_vector_mws_data.get( - "protection_area_in_ha", None - ).iloc[0] - except: - wide_scale_restoration = 0 - area_protection = 0 - - ################# Aquifer Vector ######################### - aquifer_class_map = {0: "Hard Rock", 1: "Alluvial"} - - class_to_id = {v: k for k, v in aquifer_class_map.items()} - try: - df_aquifer_vector_mws_data = sheets["aquifer_vector"][ - sheets["aquifer_vector"]["UID"] == specific_mws_id - ] - aquifer_class_name = df_aquifer_vector_mws_data.get( - "aquifer_class", None - ).iloc[0] - if aquifer_class_name == "Alluvium": - aquifer_class_name = "Alluvial" - aquifer_class = int(class_to_id.get(aquifer_class_name, "")) - except Exception: - aquifer_class = "" - - ################# SOGE Vector ######################### - Soge_class = { - 0: "Safe", - 1: "Semi-Critical", - 2: "Critical", - 3: "Over Exploited", - 4: "Not Assessed", - } - - class_to_id = {v: k for k, v in Soge_class.items()} - try: - df_soge_vector_mws_data = sheets["soge_vector"][ - sheets["soge_vector"]["UID"] == specific_mws_id - ] - soge_class_name = df_soge_vector_mws_data.get( - "class_name", None - ).iloc[0] - soge_class = int( - class_to_id.get(soge_class_name, "") - ) # Returns None if not found - except Exception: - soge_class = 4 - - ################## LCW Conflict ###################### - ## if count is 0 then Areas with no conflicts else Areas with conflicts - try: - lcw_conflict_count = sheets["lcw_conflict"][ - sheets["lcw_conflict"]["UID"] == specific_mws_id - ].shape[0] - if lcw_conflict_count == 0: - lcw_conflict = 0 - else: - lcw_conflict = 1 - except Exception as e: - lcw_conflict = 0 - - ################## mining ###################### - ## if count is 0 then Areas with no mining else Areas with mining - try: - mining_count = sheets["mining"][ - sheets["mining"]["UID"] == specific_mws_id - ].shape[0] - if mining_count == 0: - mining = 0 - else: - mining = 1 - except Exception as e: - mining = 0 - - ################## green credit ###################### - ## if count is 0 then Areas with no green credit else Areas with green credit - try: - green_credit_count = sheets["green_credit"][ - sheets["green_credit"]["UID"] == specific_mws_id - ].shape[0] - if green_credit_count == 0: - green_credit = 0 - else: - green_credit = 1 - except Exception as e: - green_credit = 0 - - ################## factory csr ###################### - ## if count is 0 then Areas with no factory else Areas with factory - try: - factory_csr_count = sheets["factory_csr"][ - sheets["factory_csr"]["UID"] == specific_mws_id - ].shape[0] - if factory_csr_count == 0: - factory_csr = 0 - else: - factory_csr = 1 - except Exception as e: - factory_csr = 0 - - ############ MWS Intersect Swb ######################## - try: - swb_df = sheets["mws_intersect_swb"] - - if swb_df is not -1 and not swb_df.empty: - mws_swb_data = swb_df[swb_df["UID"] == specific_mws_id] - - mws_intersect_swb = mws_swb_data.apply( - lambda row: { - "swbId": str(row["SWB_UID"]), - "swbName": ( - str(row["Waterbodies_name"]) - if pd.notna(row["Waterbodies_name"]) - else "" - ), - "latitude": ( - float(row["Latitude"]) - if pd.notna(row["Latitude"]) - else None - ), - "longitude": ( - float(row["Longitude"]) - if pd.notna(row["Longitude"]) - else None - ), - }, - axis=1, - ).tolist() - else: - mws_intersect_swb = [] - - except Exception as e: - print(f"Error in SWB funda: {e}") - mws_intersect_swb = [] - - ############ DEM (Digital Elevation Model) ######################## - try: - dem_df = sheets["dem"] - if dem_df is not -1 and not dem_df.empty: - mws_dem_data = dem_df[dem_df["UID"] == specific_mws_id] - - # Average of all UID mean elevations - overall_mean_elevation = dem_df["mean_elevation"].mean() - if not mws_dem_data.empty: - row = mws_dem_data.iloc[0] - relief = round( - row["max_elevation"] - row["min_elevation"], 2 - ) - mean_elevation = round(row["mean_elevation"], 2) - - # Relative mean elevation - if overall_mean_elevation != 0: - relative_mean_elevation = round( - (mean_elevation - overall_mean_elevation), 2 - ) - else: - relative_mean_elevation = 0 - - else: - relief = 0 - mean_elevation = 0 - relative_mean_elevation = 0 - - else: - relief = 0 - mean_elevation = 0 - relative_mean_elevation = 0 - - except Exception as e: - print(f"Error in getting DEM data: {e}") - relief = 0 - mean_elevation = 0 - relative_mean_elevation = 0 - - ############ Canal ######################## - try: - canal_df = sheets["canal"] - if canal_df is not -1 and not canal_df.empty: - mws_canal_data = canal_df[canal_df["UID"] == specific_mws_id] - if not mws_canal_data.empty: - canal_available = True - else: - canal_available = False - - else: - canal_available = False - - except Exception as e: - print(f"Error in getting canal data: {e}") - canal_available = False - - ############ Canal ######################## - try: - river_df = sheets["river"] - if river_df is not -1 and not river_df.empty: - mws_river_data = river_df[river_df["UID"] == specific_mws_id] - if not mws_river_data.empty: - river_available = True - else: - river_available = False - - else: - river_available = False - - except Exception as e: - print(f"Error in getting canal data: {e}") - river_available = False - - ############ lulc vector ######################## - try: - lulc_shrub_percent = 0 - lulc_forest_percent = 0 - lulc_crop_percent = 0 - - lulc_df = sheets["lulc_vector"] - - if lulc_df is not -1 and not lulc_df.empty: - - mws_lulc_data = lulc_df[lulc_df["UID"] == specific_mws_id] - - if not mws_lulc_data.empty: - - row = mws_lulc_data.iloc[0] - - # Total area - area_in_ha = float(row.get("area_in_ha", 0)) - - # Shrub - shrub_cols = [ - col - for col in lulc_df.columns - if col.startswith("shrub_scrub_in_ha_") - ] - - lulc_shrub_area = round( - sum(row[col] for col in shrub_cols) / len(shrub_cols), 2 - ) - - # Forest - forest_cols = [ - col - for col in lulc_df.columns - if col.startswith("tree_forest_in_ha_") - ] - - lulc_forest_area = round( - sum(row[col] for col in forest_cols) / len(forest_cols), - 2, - ) - - if area_in_ha > 0: - lulc_shrub_percent = round( - (lulc_shrub_area / area_in_ha) * 100, 2 - ) - - lulc_forest_percent = round( - (lulc_forest_area / area_in_ha) * 100, 2 - ) - - df_crp_intensity_mws_data = sheets["croppingIntensity_annual"][ - sheets["croppingIntensity_annual"]["UID"] == specific_mws_id - ] - - crp_row = df_crp_intensity_mws_data.iloc[0] - area_in_ha = float(crp_row.get("area_in_ha", 0)) - cropped_area_in_ha = float(crp_row.get("sum_area_in_ha", 0)) - - lulc_crop_percent = round( - (cropped_area_in_ha / area_in_ha) * 100, 2 - ) - - except Exception as e: - print(f"Error in LULC vector: {e}") - - lulc_shrub_percent = 0 - lulc_forest_percent = 0 - lulc_crop_percent = 0 - - ############ Canal ######################## - try: - drainage_density_df = sheets["drainage_density"] - if drainage_density_df is not -1 and not drainage_density_df.empty: - mws_drainage_density_data = drainage_density_df[ - drainage_density_df["UID"] == specific_mws_id - ] - if not mws_drainage_density_data.empty: - row = mws_drainage_density_data.iloc[0] - drainage_density = round(row["drainage_density"], 2) - else: - drainage_density = 0 - - else: - drainage_density = 0 - - except Exception as e: - print(f"Error in getting drainage_density data: {e}") - drainage_density = 0 - - results.append( - { - "mws_id": specific_mws_id, - "terrainCluster_ID": terrainCluster_ID, - "avg_precipitation": avg_percipitation, - "cropping_intensity_trend": cropping_intensity_trend, - "cropping_intensity_avg": cropping_intensity_avg, - "avg_single_cropped": avg_single_cropped, - "avg_double_cropped": avg_double_cropped, - "avg_triple_cropped": avg_triply_cropped, - "avg_wsr_ratio_kharif": avg_wsr_ratio_kharif, - "avg_wsr_ratio_rabi": avg_wsr_ratio_rabi, - "avg_wsr_ratio_zaid": avg_wsr_ratio_zaid, - "avg_kharif_surface_water_mws": avg_kharif_surface_water_mws, - "avg_rabi_surface_water_mws": avg_rabi_surface_water_mws, - "avg_zaid_surface_water_mws": avg_zaid_surface_water_mws, - "trend_swb": trend_swb, - "trend_g": trend_g, - "drought_category": drought_category, - "avg_number_dry_spell": avg_dry_spell_in_weeks, - "avg_runoff": round(avg_runoff, 4), - "total_nrega_assets": nrega_assets_sum, - "mws_intersect_villages": mws_intersect_villages, - "degradation_land_area": round(degradation_land_area, 4), - "increase_in_tree_cover": round(afforestation_land_area, 4), - "decrease_in_tree_cover": round(deforestation_land_area, 4), - "degradation_cropping_intensity": round( - change_in_cropping_intensity_area, 4 - ), - "urbanization_area": round(urbanization_land_area, 4), - "lulc_slope_category": lulc_slope_category, - "lulc_plain_category": lulc_plain_category, - "area_wide_scale_restoration": round(wide_scale_restoration, 4), - "area_protection": round(area_protection, 4), - "aquifer_class": aquifer_class, - "soge_class": soge_class, - "lcw_conflict": lcw_conflict, - "mining": mining, - "green_credit": green_credit, - "factory_csr": factory_csr, - "mws_intersect_swb": mws_intersect_swb, - "relief": relief, - "mean_elevation": mean_elevation, - "relative_mean_elevation": relative_mean_elevation, - "canal_available": canal_available, - "river_available": river_available, - "lulc_shrub_percent": lulc_shrub_percent, - "lulc_forest_percent": lulc_forest_percent, - "lulc_crop_percent": lulc_crop_percent, - "drainage_density": drainage_density, - } - ) - - results_df = pd.DataFrame(results) - if file_type == "xlsx": - results_df.to_excel(file_xl_path + "_KYL_filter_data.xlsx", index=False) - elif file_type == "json": - results_list = results_df.to_dict(orient="records") - with open(file_xl_path + "_KYL_filter_data.json", "w") as json_file: - json.dump(results_list, json_file, indent=4) - elif file_type == "geojson": - layer_name = "deltaG_well_depth_" + district + "_" + block - mws_annual_geojson = get_url("mws_layers", layer_name) - response = requests.get(mws_annual_geojson) - response.raise_for_status() - - # Check if response has content - if response.content: - geojson_data = response.json() - deltaG_geojson = file_xl_path + "_deltaG_annual.geojson" - - with open(deltaG_geojson, "w") as f: - json.dump(geojson_data, f) - create_geojson_for_all_mws( - deltaG_geojson, - results_df, - file_xl_path + "_KYL_filter_data.geojson", - ) - file_path = get_mws_KYL_filter_data(state, district, block, file_type) - - except Exception as e: - return Response( - { - "status": "error", - "message": f"Error during file generation: {str(e)}", - }, - status=status.HTTP_500_INTERNAL_SERVER_ERROR, - ) - if file_path: - content_type_map = { - "xlsx": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", - "json": "application/json", - "geojson": "application/geo+json", - } - content_type = content_type_map.get(file_type, "application/octet-stream") - - with open(file_path, "rb") as file: - response = HttpResponse( - file.read(), - content_type=content_type, - ) - response["Content-Disposition"] = ( - f"attachment; filename={district}_{block}_KYL_filter_data.{file_type}" - ) - return response - - else: - return Response( - {"status": "error", "message": "Failed to generate or download file"}, - status=status.HTTP_404_NOT_FOUND, - ) - - -def get_mws_KYL_filter_data(state, district, block, file_type): - state_folder = state.replace(" ", "_").upper() - district_folder = district.replace(" ", "_").upper() - file_xl_path = os.path.join( - EXCEL_PATH, - "data/stats_excel_files", - state_folder, - district_folder, - f"{district}_{block}", - ) - - file_path = None - if file_type == "xlsx": - file_path = os.path.join(file_xl_path + "_KYL_filter_data.xlsx") - elif file_type == "json": - file_path = os.path.join(file_xl_path + "_KYL_filter_data.json") - elif file_type == "geojson": - file_path = os.path.join(file_xl_path + "_KYL_filter_data.geojson") - - if os.path.exists(file_path): - return file_path - else: - return None +import os +import requests, json +import pandas as pd +import pymannkendall as mk +import numpy as np +import ast +from nrm_app.settings import EXCEL_PATH +from .utils import get_url +from rest_framework.response import Response +from rest_framework import status +from django.http import HttpResponse +from .models import LayerInfo + + +def create_geojson_for_all_mws(existing_geojson_path, df, new_geojson_path): + with open(existing_geojson_path) as f: + existing_data = json.load(f) + + features = [] + + for _, row in df.iterrows(): + uid = row["mws_id"] + geometry = None + + for feature in existing_data["features"]: + if feature["properties"].get("uid") == uid: + geometry = feature["geometry"] + break + + if geometry is None: + print( + f"No geometry found for uid: {uid}. Using default geometry (e.g., None)." + ) + geometry = {"type": "Point", "coordinates": [0, 0]} + + properties = row.to_dict() + + new_feature = { + "type": "Feature", + "geometry": geometry, + "properties": properties, + } + features.append(new_feature) + + new_feature_collection = {"type": "FeatureCollection", "features": features} + + with open(new_geojson_path, "w") as f: + json.dump(new_feature_collection, f) + + +def generate_mws_data_for_kyl_filters( + state, district, block, file_type, regenerate=None +): + state_folder = state.replace(" ", "_").upper() + district_folder = district.replace(" ", "_").upper() + file_xl_path = os.path.join( + EXCEL_PATH, + "data/stats_excel_files", + state_folder, + district_folder, + f"{district}_{block}", + ) + if regenerate: + file_path = None + else: + file_path = get_mws_KYL_filter_data(state, district, block, file_type) + if not file_path: + try: + sheets = { + "hydrological_annual": -1, + "terrain": -1, + "croppingIntensity_annual": -1, + "surfaceWaterBodies_annual": -1, + "croppingDrought_kharif": -1, + "nrega_annual": -1, + "mws_intersect_villages": -1, + "change_detection_degradation": -1, + "change_detection_afforestation": -1, + "change_detection_deforestation": -1, + "change_detection_urbanization": -1, + "change_detection_cropintensity": -1, + "terrain_lulc_slope": -1, + "terrain_lulc_plain": -1, + "restoration_vector": -1, + "aquifer_vector": -1, + "soge_vector": -1, + "lcw_conflict": -1, + "factory_csr": -1, + "mining": -1, + "green_credit": -1, + "mws_intersect_swb": -1, + "dem": -1, + "canal": -1, + "river": -1, + "lulc_vector": -1, + "drainage_density": -1, + } + + try: + with pd.ExcelFile(file_xl_path + ".xlsx") as xl: + available_sheets = xl.sheet_names # Get list of available sheets + + # Try to parse each sheet if it exists + for sheet_name in sheets.keys(): + if sheet_name in available_sheets: + try: + sheets[sheet_name] = xl.parse(sheet_name) + except Exception as e: + print(f"Error parsing sheet {sheet_name}: {e}") + sheets[sheet_name] = -1 + else: + print(f"Sheet {sheet_name} not found in Excel file") + sheets[sheet_name] = -1 + + except Exception as e: + print(f"Error reading Excel file: {e}") + # Return all sheets as -1 if the file can't be read + return {k: -1 for k in sheets.keys()} + + results = [] + df_hydrological_annual = sheets["hydrological_annual"] + + for specific_mws_id in df_hydrological_annual["UID"].unique(): + hydro_annual_mws_data = df_hydrological_annual[ + df_hydrological_annual["UID"] == specific_mws_id + ] + precipitation_columns = hydro_annual_mws_data.filter( + like="Precipitation" + ) # Avg_precipitation + total_percipitation_column = precipitation_columns.shape[1] + sum_precipitation = precipitation_columns.sum(axis=1).sum() + avg_percipitation = round( + sum_precipitation / total_percipitation_column, 4 + ) + + try: + terrain_vector_mws_data = sheets["terrain"][ + sheets["terrain"]["UID"] == specific_mws_id + ] + terrainCluster_ID = terrain_vector_mws_data.get( + "terrain_cluster_id", None + ).iloc[ + 0 + ] # terrain + except: + terrainCluster_ID = -9999 + + try: + df_crp_intensity_mws_data = sheets["croppingIntensity_annual"][ + sheets["croppingIntensity_annual"]["UID"] == specific_mws_id + ] + df_crp_intensity_mws_data = df_crp_intensity_mws_data.fillna(0) + + crp_Intensity_columns = df_crp_intensity_mws_data.filter( + like="cropping_intensity_unit_less" + ) # cropping_intensity_avg + total_crp_Intensity_column = crp_Intensity_columns.shape[1] + sum_crp_Intensity = crp_Intensity_columns.sum(axis=1).sum() + cropping_intensity_avg = round( + ( + sum_crp_Intensity / total_crp_Intensity_column + if total_crp_Intensity_column > 0 + else 0 + ), + 4, + ) + + ######### Cropping Intensity Trend ################# + crp_intensity_T = df_crp_intensity_mws_data.filter( + like="cropping_intensity_unit_less" + ).dropna() # Drop rows with NaN for trend calculation + crp_intensity_T = crp_intensity_T.squeeze().tolist()[:-3] + result = mk.original_test(crp_intensity_T) + + def sens_slope(data): + slopes = [] + for i in range(len(data) - 1): + for j in range(i + 1, len(data)): + s = (data[j] - data[i]) / (j - i) + slopes.append(s) + return np.median(slopes) + + cropping_intensity_trend_value = sens_slope(crp_intensity_T) + cropping_intensity_trend = None + if result.trend == "no trend": + cropping_intensity_trend = "0" + elif result.trend == "increasing": + cropping_intensity_trend = "1" + else: + cropping_intensity_trend = "-1" + + # Total cropped area, replace NaN with 0 for these columns as well + total_cropped_area = df_crp_intensity_mws_data.iloc[0][ + "sum_area_in_ha" + ] + + # Handle single-cropped area calculation + single_crop_columns = df_crp_intensity_mws_data.filter( + like="single_cropped_area" + ) # avg_single_cropped + total_single_crop_column = single_crop_columns.shape[1] + sum_single_crop = single_crop_columns.sum(axis=1).sum() + percent_single_crop = ( + sum_single_crop * 100 / total_cropped_area + if total_cropped_area > 0 + else 0 + ) + avg_single_cropped = round( + ( + percent_single_crop / total_single_crop_column + if total_single_crop_column > 0 + else 0 + ), + 4, + ) + + # Handle doubly-cropped area calculation + double_crop_columns = df_crp_intensity_mws_data.filter( + like="doubly_cropped_area" + ) # avg_double_cropped + total_double_crop_column = double_crop_columns.shape[1] + sum_double_crop = double_crop_columns.sum(axis=1).sum() + percent_double_crop = ( + sum_double_crop * 100 / total_cropped_area + if total_cropped_area > 0 + else 0 + ) + avg_double_cropped = round( + ( + percent_double_crop / total_double_crop_column + if total_double_crop_column > 0 + else 0 + ), + 4, + ) + + # Handle triply-cropped area calculation + triply_crop_columns = df_crp_intensity_mws_data.filter( + like="triply_cropped_area" + ) # avg_triply_cropped + total_triply_crop_column = triply_crop_columns.shape[1] + sum_triply_crop = triply_crop_columns.sum(axis=1).sum() + percent_triply_crop = ( + sum_triply_crop * 100 / total_cropped_area + if total_cropped_area > 0 + else 0 + ) + avg_triply_cropped = round( + ( + percent_triply_crop / total_triply_crop_column + if total_triply_crop_column > 0 + else 0 + ), + 4, + ) + + except Exception as e: + # Handle exception and ensure all variables are set + cropping_intensity_avg = -9999 + cropping_intensity_trend = -9999 + avg_single_cropped = -9999 + avg_double_cropped = -9999 + avg_triply_cropped = -9999 + print(f"Error occurred: {e}") + + + ##################### SWB ##################### + try: + df_swb_annual_mws_data = sheets["surfaceWaterBodies_annual"][ + sheets["surfaceWaterBodies_annual"]["UID"] == specific_mws_id + ] + df_crp_intensity_mws_data = sheets["croppingIntensity_annual"][ + sheets["croppingIntensity_annual"]["UID"] == specific_mws_id + ] + + df_crp_intensity_mws_data = df_crp_intensity_mws_data.fillna(0) + df_swb_annual_mws_data = df_swb_annual_mws_data.fillna(0) + swb_area_kharif_columns = df_swb_annual_mws_data.filter( + like="kharif_area" + ) + single_kharif_crop_columns = df_crp_intensity_mws_data.filter( + like="single_kharif_cropped_area" + ) + double_crop_columns = df_crp_intensity_mws_data.filter( + like="doubly_cropped_area" + ) + triply_crop_columns = df_crp_intensity_mws_data.filter( + like="triply_cropped_area" + ) + + combined_columns_kharif = single_kharif_crop_columns.add( + double_crop_columns, fill_value=0 + ) + combined_columns_kharif = combined_columns_kharif.add( + triply_crop_columns, fill_value=0 + ) + total_cropped_area_kharif = combined_columns_kharif.sum( + axis=1 + ).sum() + total_swb_area_kharif_column = swb_area_kharif_columns.shape[1] + sum_swb_area_kharif = swb_area_kharif_columns.sum(axis=1).sum() + + avg_wsr_ratio_kharif = ( + sum_swb_area_kharif / total_cropped_area_kharif + if total_cropped_area_kharif > 0 + else 0 + ) + avg_wsr_ratio_kharif = round( + avg_wsr_ratio_kharif * 100 / total_swb_area_kharif_column, 4 + ) + swb_area_rabi_columns = df_swb_annual_mws_data.filter( + like="rabi_area" + ) + single_non_kharif_crop_columns = df_crp_intensity_mws_data.filter( + like="single_non_kharif_cropped_area" + ) + + # Combine the cropping areas and calculate total cropped area for Rabi + combined_columns_rabi = single_non_kharif_crop_columns.add( + double_crop_columns, fill_value=0 + ) + combined_columns_rabi = combined_columns_rabi.add( + triply_crop_columns, fill_value=0 + ) + total_cropped_area_rabi = combined_columns_rabi.sum(axis=1).sum() + + total_swb_rabi_column = swb_area_rabi_columns.shape[1] + sum_swb_area_rabi = swb_area_rabi_columns.sum(axis=1).sum() + + # Average WSR ratio for Rabi + avg_wsr_ratio_rabi = ( + sum_swb_area_rabi / total_cropped_area_rabi + if total_cropped_area_rabi > 0 + else 0 + ) + avg_wsr_ratio_rabi = round( + avg_wsr_ratio_rabi * 100 / total_swb_rabi_column, 4 + ) + swb_area_zaid_columns = df_swb_annual_mws_data.filter( + like="zaid_area" + ) + total_cropped_area_zaid = triply_crop_columns.sum(axis=1).sum() + + total_swb_zaid_column = swb_area_zaid_columns.shape[1] + sum_swb_area_zaid = swb_area_zaid_columns.sum(axis=1).sum() + avg_wsr_ratio_zaid = ( + sum_swb_area_zaid / total_cropped_area_zaid + if total_cropped_area_zaid > 0 + else 0 + ) + avg_wsr_ratio_zaid = round( + avg_wsr_ratio_zaid * 100 / total_swb_zaid_column, 4 + ) + + except Exception as e: + avg_wsr_ratio_kharif = -9999 + avg_wsr_ratio_rabi = -9999 + avg_wsr_ratio_zaid = -9999 + print(f"Error occurred: {e}") + + ############ Swb_average + avg_kharif_surface_water_mws = -9999 + avg_rabi_surface_water_mws = -9999 + avg_zaid_surface_water_mws = -9999 + try: + df_swb_annual_mws_data = sheets["surfaceWaterBodies_annual"][ + sheets["surfaceWaterBodies_annual"]["UID"] == specific_mws_id + ] + if not df_swb_annual_mws_data.empty: + total_swb_area = df_swb_annual_mws_data.iloc[0][ + "total_swb_area_in_ha" + ] + + if total_swb_area != 0: # Check if total_swb_area is not zero + swb_area_kharif_columns = df_swb_annual_mws_data.filter( + like="kharif_area" + ) + total_swb_area_kharif_column = swb_area_kharif_columns.shape[1] + sum_swb_area_kharif = ( + swb_area_kharif_columns.sum(axis=1).sum() / total_swb_area + ) + avg_kharif_surface_water_mws = round( + ( + sum_swb_area_kharif * 100 / total_swb_area_kharif_column + if total_swb_area_kharif_column > 0 + else 0 + ), + 4, + ) + + swb_rabi_area_columns = df_swb_annual_mws_data.filter( + like="rabi_area" + ) + total_swb_rabi_area_column = swb_rabi_area_columns.shape[1] + sum_swb_rabi_area = ( + swb_rabi_area_columns.sum(axis=1).sum() / total_swb_area + ) + avg_rabi_surface_water_mws = round( + ( + sum_swb_rabi_area * 100 / total_swb_rabi_area_column + if total_swb_rabi_area_column > 0 + else 0 + ), + 4, + ) + + swb_zaid_area_columns = df_swb_annual_mws_data.filter( + like="zaid_area" + ) + total_swb_zaid_area_column = swb_zaid_area_columns.shape[1] + sum_swb_zaid_area = ( + swb_zaid_area_columns.sum(axis=1).sum() / total_swb_area + ) + avg_zaid_surface_water_mws = round( + ( + sum_swb_zaid_area * 100 / total_swb_zaid_area_column + if total_swb_zaid_area_column > 0 + else 0 + ), + 4, + ) + else: + avg_perc_kharif_surface_water_mws = ( + avg_perc_rabi_surface_water_mws + ) = avg_perc_zaid_surface_water_mws = 0 + + except: + avg_perc_kharif_surface_water_mws = ( + avg_perc_rabi_surface_water_mws + ) = avg_perc_zaid_surface_water_mws = -9999 + + ################# SWB Trend ###################### + try: + df_swb_annual_mws_data = sheets["surfaceWaterBodies_annual"][ + sheets["surfaceWaterBodies_annual"]["UID"] == specific_mws_id + ] + swb_T = df_swb_annual_mws_data.filter( + like="total_area_in_ha" + ).dropna() # Drop rows with NaN for trend calculation + swb_T = swb_T.iloc[0].dropna().tolist() + result = mk.original_test(swb_T) + + trend_swb = None + if result.trend == "no trend": + trend_swb = "0" + elif result.trend == "increasing": + trend_swb = "1" + else: + trend_swb = "-1" + except: + trend_swb = -9999 + + ######### G Trend ################# + try: + G_Trend = ( + hydro_annual_mws_data.filter(like="G") + .drop(columns=hydro_annual_mws_data.filter(like="DeltaG").columns) + .dropna() + ) + G_Trend = G_Trend.squeeze().tolist() + result = mk.original_test(G_Trend) + + def sens_slope(data): + slopes = [] + for i in range(len(data) - 1): + for j in range(i + 1, len(data)): + s = (data[j] - data[i]) / (j - i) + slopes.append(s) + return np.median(slopes) + + trend_g_value = sens_slope(G_Trend) + trend_g = None + if result.trend == "no trend": + trend_g = "0" + elif result.trend == "increasing": + trend_g = "1" + else: + trend_g = "-1" + except: + trend_g = -9999 + + ######### drought_category ############## + try: + + layers = LayerInfo.objects.get( + layer_type="vector", workspace="drought" + ) + years = [ + str(year) + for year in range(layers.start_year, layers.end_year + 1) + ] + + df_crpDrought_mws_data = sheets["croppingDrought_kharif"][ + sheets["croppingDrought_kharif"]["UID"] == specific_mws_id + ] + + sum_moderate_severe = { + year: ( + 1 + if ( + df_crpDrought_mws_data.iloc[0][ + f"Moderate_in_weeks_{year}" + ] + + df_crpDrought_mws_data.iloc[0][ + f"Severe_in_weeks_{year}" + ] + ) + >= 5 + else 0 + ) + for year in years + } + sum_of_values = sum(sum_moderate_severe.values()) + drought_category = None + if sum_of_values >= 2: + drought_category = 2 + else: + drought_category = sum_of_values + + ######## avg_dry_spell_in_weeks + dryspell_columns = df_crpDrought_mws_data.filter( + like="drysp_unit_4_weeks" + ) # avg_dry_spell_in_weeks + total_dryspell_column = dryspell_columns.shape[1] + sum_dryspell = dryspell_columns.sum(axis=1).sum() + avg_dry_spell_in_weeks = round( + ( + sum_dryspell / total_dryspell_column + if total_dryspell_column > 0 + else 0 + ), + 4, + ) + except: + drought_category = -9999 + avg_dry_spell_in_weeks = -9999 + + ################# avg_runoff + runoff_columns = hydro_annual_mws_data.filter( + like="RunOff" + ) # avg_runoff + total_runoff_column = runoff_columns.shape[1] + sum_runoff = runoff_columns.sum(axis=1).sum() + avg_runoff = sum_runoff / total_runoff_column + + ############## Nrega Asset ########################## + try: + df_nrega_assets_mws_data = sheets["nrega_annual"][ + sheets["nrega_annual"]["mws_id"] == specific_mws_id + ] + nrega_assets_sum = ( + df_nrega_assets_mws_data.iloc[:, 1:] + .select_dtypes(include="number") + .sum() + .sum() + ) + except: + nrega_assets_sum = -9999 + + ############ MWS Intersect Villages ######################## + try: + df_mws_inters_villages_mws_data = sheets["mws_intersect_villages"][ + sheets["mws_intersect_villages"]["MWS UID"] == specific_mws_id + ] + mws_intersect_villages = df_mws_inters_villages_mws_data.get( + "Village IDs", None + ).iloc[0] + mws_intersect_villages = ast.literal_eval(mws_intersect_villages) + except: + mws_intersect_villages = [] + + ############ Change Detection Degradation ################### + try: + df_change_degr_detection_mws_data = sheets[ + "change_detection_degradation" + ][sheets["change_detection_degradation"]["UID"] == specific_mws_id] + degr_sum = ( + df_change_degr_detection_mws_data[ + [ + "farm_to_barren_area_in_ha", + "farm_to_scrub_land_area_in_ha", + ] + ] + .sum(axis=1) + .iloc[0] + ) + df_change_crp_detection_mws_data = sheets[ + "change_detection_cropintensity" + ][ + sheets["change_detection_cropintensity"]["UID"] + == specific_mws_id + ] + crp_sum = ( + df_change_crp_detection_mws_data[ + [ + "double_to_single_area_in_ha", + "triple_to_double_area_in_ha", + "triple_to_single_area_in_ha", + ] + ] + .sum(axis=1) + .iloc[0] + ) + degradation_land_area = degr_sum + crp_sum + change_in_cropping_intensity_area = ( + df_change_crp_detection_mws_data.get( + "total_change_crop_intensity_area_in_ha", None + ).iloc[0] + ) + + except: + degradation_land_area = -9999 + change_in_cropping_intensity_area = -9999 + + ############ Change Detection Afforestation ################### + try: + df_change_affo_detection_mws_data = sheets[ + "change_detection_afforestation" + ][ + sheets["change_detection_afforestation"]["UID"] + == specific_mws_id + ] + afforestation_column = [ + "barren_to_forest_area_in_ha", + "farm_to_forest_area_in_ha", + ] + afforestation_land_area = df_change_affo_detection_mws_data.get( + "total_afforestation_area_in_ha", None + ).iloc[0] + except: + afforestation_land_area = -9999 + + ############ Change Detection Deforestation ################### + try: + df_change_defo_detection_mws_data = sheets[ + "change_detection_deforestation" + ][ + sheets["change_detection_deforestation"]["UID"] + == specific_mws_id + ] + deforestation_land_area = df_change_defo_detection_mws_data.get( + "total_deforestation_area_in_ha", None + ).iloc[0] + except: + deforestation_land_area = -9999 + + ############ Change Detection Urbanization ################### + try: + df_change_urba_detection_mws_data = sheets[ + "change_detection_urbanization" + ][sheets["change_detection_urbanization"]["UID"] == specific_mws_id] + urbanization_land_area = df_change_urba_detection_mws_data.get( + "total_urbanization_area_in_ha", None + ).iloc[0] + except: + urbanization_land_area = -9999 + + ############# Terrain lulc slope / plain ##################### + try: + df_lulc_slope_mws_data = sheets["terrain_lulc_slope"][ + sheets["terrain_lulc_slope"]["UID"] == specific_mws_id + ] + lulc_slope_category = ( + df_lulc_slope_mws_data.get("cluster_name", pd.NA).iloc[0] + if not df_lulc_slope_mws_data.empty + else None + ) + + except: + lulc_slope_category = -9999 + + try: + df_lulc_plain_mws_data = sheets["terrain_lulc_plain"][ + sheets["terrain_lulc_plain"]["UID"] == specific_mws_id + ] + lulc_plain_category = ( + df_lulc_plain_mws_data.get("cluster_name", pd.NA).iloc[0] + if not df_lulc_plain_mws_data.empty + else None + ) + + except: + lulc_plain_category = -9999 + + ################# Restoration Vector ######################### + try: + df_restoration_vector_mws_data = sheets["restoration_vector"][ + sheets["restoration_vector"]["UID"] == specific_mws_id + ] + wide_scale_restoration = df_restoration_vector_mws_data.get( + "wide_scale_restoration_area_in_ha", None + ).iloc[0] + area_protection = df_restoration_vector_mws_data.get( + "protection_area_in_ha", None + ).iloc[0] + except: + wide_scale_restoration = -9999 + area_protection = -9999 + + ################# Aquifer Vector ######################### + aquifer_class_map = {0: "Hard Rock", 1: "Alluvial"} + + class_to_id = {v: k for k, v in aquifer_class_map.items()} + try: + df_aquifer_vector_mws_data = sheets["aquifer_vector"][ + sheets["aquifer_vector"]["UID"] == specific_mws_id + ] + aquifer_class_name = df_aquifer_vector_mws_data.get( + "aquifer_class", None + ).iloc[0] + if aquifer_class_name == "Alluvium": + aquifer_class_name = "Alluvial" + aquifer_class = int(class_to_id.get(aquifer_class_name, "")) + except Exception: + aquifer_class = -9999 + + ################# SOGE Vector ######################### + Soge_class = { + 0: "Safe", + 1: "Semi-Critical", + 2: "Critical", + 3: "Over Exploited", + 4: "Not Assessed", + } + + class_to_id = {v: k for k, v in Soge_class.items()} + try: + df_soge_vector_mws_data = sheets["soge_vector"][ + sheets["soge_vector"]["UID"] == specific_mws_id + ] + soge_class_name = df_soge_vector_mws_data.get( + "class_name", None + ).iloc[0] + soge_class = int( + class_to_id.get(soge_class_name, "") + ) # Returns None if not found + except Exception: + soge_class = -9999 + + ################## LCW Conflict ###################### + ## if count is 0 then Areas with no conflicts else Areas with conflicts + try: + lcw_conflict_count = sheets["lcw_conflict"][ + sheets["lcw_conflict"]["UID"] == specific_mws_id + ].shape[0] + if lcw_conflict_count == 0: + lcw_conflict = 0 + else: + lcw_conflict = 1 + except Exception as e: + lcw_conflict = -9999 + + ################## mining ###################### + ## if count is 0 then Areas with no mining else Areas with mining + try: + mining_count = sheets["mining"][ + sheets["mining"]["UID"] == specific_mws_id + ].shape[0] + if mining_count == 0: + mining = 0 + else: + mining = 1 + except Exception as e: + mining = -9999 + + ################## green credit ###################### + ## if count is 0 then Areas with no green credit else Areas with green credit + try: + green_credit_count = sheets["green_credit"][ + sheets["green_credit"]["UID"] == specific_mws_id + ].shape[0] + if green_credit_count == 0: + green_credit = 0 + else: + green_credit = 1 + except Exception as e: + green_credit = -9999 + + ################## factory csr ###################### + ## if count is 0 then Areas with no factory else Areas with factory + try: + factory_csr_count = sheets["factory_csr"][ + sheets["factory_csr"]["UID"] == specific_mws_id + ].shape[0] + if factory_csr_count == 0: + factory_csr = 0 + else: + factory_csr = 1 + except Exception as e: + factory_csr = -9999 + + ############ MWS Intersect Swb ######################## + try: + swb_df = sheets["mws_intersect_swb"] + + if swb_df is not -1 and not swb_df.empty: + mws_swb_data = swb_df[swb_df["UID"] == specific_mws_id] + + mws_intersect_swb = mws_swb_data.apply( + lambda row: { + "swbId": str(row["SWB_UID"]), + "swbName": ( + str(row["Waterbodies_name"]) + if pd.notna(row["Waterbodies_name"]) + else "" + ), + "latitude": ( + float(row["Latitude"]) + if pd.notna(row["Latitude"]) + else None + ), + "longitude": ( + float(row["Longitude"]) + if pd.notna(row["Longitude"]) + else None + ), + }, + axis=1, + ).tolist() + else: + mws_intersect_swb = [] + + except Exception as e: + print(f"Error in SWB funda: {e}") + mws_intersect_swb = [] + + ############ DEM (Digital Elevation Model) ######################## + try: + dem_df = sheets["dem"] + if dem_df is not -1 and not dem_df.empty: + mws_dem_data = dem_df[dem_df["UID"] == specific_mws_id] + + # Average of all UID mean elevations + overall_mean_elevation = dem_df["mean_elevation_in_m"].mean() + if not mws_dem_data.empty: + row = mws_dem_data.iloc[0] + relief = round( + row["max_elevation_in_m"] - row["min_elevation_in_m"], 2 + ) + mean_elevation = round(row["mean_elevation_in_m"], 2) + + # Relative mean elevation + if overall_mean_elevation != 0: + relative_mean_elevation = round( + (mean_elevation - overall_mean_elevation), 2 + ) + else: + relative_mean_elevation = 0 + + else: + relief = 0 + mean_elevation = 0 + relative_mean_elevation = 0 + + else: + relief = -9999 + mean_elevation = -9999 + relative_mean_elevation = -9999 + + except Exception as e: + print(f"Error in getting DEM data: {e}") + relief = -9999 + mean_elevation = -9999 + relative_mean_elevation = -9999 + + ############ Canal ######################## + try: + canal_df = sheets["canal"] + if canal_df is not -1 and not canal_df.empty: + mws_canal_data = canal_df[canal_df["UID"] == specific_mws_id] + if not mws_canal_data.empty: + canal_available = True + else: + canal_available = False + + else: + canal_available = False + + except Exception as e: + print(f"Error in getting canal data: {e}") + canal_available = -9999 + + ############ Canal ######################## + try: + river_df = sheets["river"] + if river_df is not -1 and not river_df.empty: + mws_river_data = river_df[river_df["UID"] == specific_mws_id] + if not mws_river_data.empty: + river_available = True + else: + river_available = False + + else: + river_available = False + + except Exception as e: + print(f"Error in getting canal data: {e}") + river_available = -9999 + + ############ lulc vector ######################## + try: + lulc_shrub_percent = -9999 + lulc_forest_percent = -9999 + lulc_crop_percent = -9999 + + lulc_df = sheets["lulc_vector"] + + if lulc_df is not -1 and not lulc_df.empty: + + mws_lulc_data = lulc_df[lulc_df["UID"] == specific_mws_id] + + if not mws_lulc_data.empty: + + row = mws_lulc_data.iloc[0] + + # Total area + area_in_ha = float(row.get("area_in_ha", 0)) + + # Shrub + shrub_cols = [ + col + for col in lulc_df.columns + if col.startswith("shrub_scrub_in_ha_") + ] + + lulc_shrub_area = round( + sum(row[col] for col in shrub_cols) / len(shrub_cols), 2 + ) + + # Forest + forest_cols = [ + col + for col in lulc_df.columns + if col.startswith("tree_forest_in_ha_") + ] + + lulc_forest_area = round( + sum(row[col] for col in forest_cols) / len(forest_cols), + 2, + ) + + if area_in_ha > 0: + lulc_shrub_percent = round( + (lulc_shrub_area / area_in_ha) * 100, 2 + ) + + lulc_forest_percent = round( + (lulc_forest_area / area_in_ha) * 100, 2 + ) + + df_crp_intensity_mws_data = sheets["croppingIntensity_annual"][ + sheets["croppingIntensity_annual"]["UID"] == specific_mws_id + ] + + crp_row = df_crp_intensity_mws_data.iloc[0] + area_in_ha = float(crp_row.get("area_in_ha", 0)) + cropped_area_in_ha = float(crp_row.get("sum_area_in_ha", 0)) + + lulc_crop_percent = round( + (cropped_area_in_ha / area_in_ha) * 100, 2 + ) + + else: + lulc_shrub_percent = -9999 + lulc_forest_percent = -9999 + lulc_crop_percent = -9999 + except Exception as e: + print(f"Error in LULC vector: {e}") + + lulc_shrub_percent = -9999 + lulc_forest_percent = -9999 + lulc_crop_percent = -9999 + + ############ Canal ######################## + try: + drainage_density_df = sheets["drainage_density"] + if drainage_density_df is not -1 and not drainage_density_df.empty: + mws_drainage_density_data = drainage_density_df[ + drainage_density_df["UID"] == specific_mws_id + ] + if not mws_drainage_density_data.empty: + row = mws_drainage_density_data.iloc[0] + drainage_density = round(row["drainage_density_std_in_km_per_km2"], 2) + else: + drainage_density = 0 + + else: + drainage_density = -9999 + + + except Exception as e: + print(f"Error in getting drainage_density data: {e}") + drainage_density = -9999 + + results.append( + { + "mws_id": specific_mws_id, + "terrainCluster_ID": terrainCluster_ID, + "avg_precipitation": avg_percipitation, + "cropping_intensity_trend": cropping_intensity_trend, + "cropping_intensity_avg": cropping_intensity_avg, + "avg_single_cropped": avg_single_cropped, + "avg_double_cropped": avg_double_cropped, + "avg_triple_cropped": avg_triply_cropped, + "avg_wsr_ratio_kharif": avg_wsr_ratio_kharif, + "avg_wsr_ratio_rabi": avg_wsr_ratio_rabi, + "avg_wsr_ratio_zaid": avg_wsr_ratio_zaid, + "avg_kharif_surface_water_mws": avg_kharif_surface_water_mws, + "avg_rabi_surface_water_mws": avg_rabi_surface_water_mws, + "avg_zaid_surface_water_mws": avg_zaid_surface_water_mws, + "trend_swb": trend_swb, + "trend_g": trend_g, + "drought_category": drought_category, + "avg_number_dry_spell": avg_dry_spell_in_weeks, + "avg_runoff": round(avg_runoff, 4), + "total_nrega_assets": nrega_assets_sum, + "mws_intersect_villages": mws_intersect_villages, + "degradation_land_area": round(degradation_land_area, 4), + "increase_in_tree_cover": round(afforestation_land_area, 4), + "decrease_in_tree_cover": round(deforestation_land_area, 4), + "degradation_cropping_intensity": round( + change_in_cropping_intensity_area, 4 + ), + "urbanization_area": round(urbanization_land_area, 4), + "lulc_slope_category": lulc_slope_category, + "lulc_plain_category": lulc_plain_category, + "area_wide_scale_restoration": round(wide_scale_restoration, 4), + "area_protection": round(area_protection, 4), + "aquifer_class": aquifer_class, + "soge_class": soge_class, + "lcw_conflict": lcw_conflict, + "mining": mining, + "green_credit": green_credit, + "factory_csr": factory_csr, + "mws_intersect_swb": mws_intersect_swb, + "relief": relief, + "mean_elevation": mean_elevation, + "relative_mean_elevation": relative_mean_elevation, + "canal_available": canal_available, + "river_available": river_available, + "lulc_shrub_percent": lulc_shrub_percent, + "lulc_forest_percent": lulc_forest_percent, + "lulc_crop_percent": lulc_crop_percent, + "drainage_density": drainage_density, + } + ) + + results_df = pd.DataFrame(results) + if file_type == "xlsx": + results_df.to_excel(file_xl_path + "_KYL_filter_data.xlsx", index=False) + elif file_type == "json": + results_list = results_df.to_dict(orient="records") + with open(file_xl_path + "_KYL_filter_data.json", "w") as json_file: + json.dump(results_list, json_file, indent=4) + elif file_type == "geojson": + layer_name = "deltaG_well_depth_" + district + "_" + block + mws_annual_geojson = get_url("mws_layers", layer_name) + response = requests.get(mws_annual_geojson) + response.raise_for_status() + + # Check if response has content + if response.content: + geojson_data = response.json() + deltaG_geojson = file_xl_path + "_deltaG_annual.geojson" + + with open(deltaG_geojson, "w") as f: + json.dump(geojson_data, f) + create_geojson_for_all_mws( + deltaG_geojson, + results_df, + file_xl_path + "_KYL_filter_data.geojson", + ) + file_path = get_mws_KYL_filter_data(state, district, block, file_type) + + except Exception as e: + return Response( + { + "status": "error", + "message": f"Error during file generation: {str(e)}", + }, + status=status.HTTP_500_INTERNAL_SERVER_ERROR, + ) + if file_path: + content_type_map = { + "xlsx": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", + "json": "application/json", + "geojson": "application/geo+json", + } + content_type = content_type_map.get(file_type, "application/octet-stream") + + with open(file_path, "rb") as file: + response = HttpResponse( + file.read(), + content_type=content_type, + ) + response["Content-Disposition"] = ( + f"attachment; filename={district}_{block}_KYL_filter_data.{file_type}" + ) + return response + + else: + return Response( + {"status": "error", "message": "Failed to generate or download file"}, + status=status.HTTP_404_NOT_FOUND, + ) + + +def get_mws_KYL_filter_data(state, district, block, file_type): + state_folder = state.replace(" ", "_").upper() + district_folder = district.replace(" ", "_").upper() + file_xl_path = os.path.join( + EXCEL_PATH, + "data/stats_excel_files", + state_folder, + district_folder, + f"{district}_{block}", + ) + + file_path = None + if file_type == "xlsx": + file_path = os.path.join(file_xl_path + "_KYL_filter_data.xlsx") + elif file_type == "json": + file_path = os.path.join(file_xl_path + "_KYL_filter_data.json") + elif file_type == "geojson": + file_path = os.path.join(file_xl_path + "_KYL_filter_data.geojson") + + if os.path.exists(file_path): + return file_path + else: + return None diff --git a/stats_generator/utils.py b/stats_generator/utils.py index 6a38a648..43756108 100644 --- a/stats_generator/utils.py +++ b/stats_generator/utils.py @@ -1,2202 +1,2281 @@ -import os -import requests, json -from django.http import HttpResponse, Http404 -from rest_framework.response import Response -import pandas as pd -import geopandas as gpd -from collections import defaultdict -from datetime import datetime -from nrm_app.settings import GEOSERVER_URL, EXCEL_PATH -import numpy as np -from shapely.geometry import Point, shape -from .models import LayerInfo -from django.http import HttpResponse -from rest_framework import status -from pathlib import Path - - -def fetch_layers_for_excel_generation(): - """ - Fetch all vector layers where `excel_to_be_generated` is True. - """ - layers = LayerInfo.objects.filter( - layer_type="vector", excel_to_be_generated=True - ).values("layer_name", "workspace", "start_year", "end_year") - return list(layers) - - -def get_url(workspace, layer_name): - """Construct the GeoServer WFS request URL for fetching GeoJSON data.""" - geojson_url = f"{GEOSERVER_URL}/{workspace}/ows?service=WFS&version=1.0.0&request=GetFeature&typeName={workspace}:{layer_name}&outputFormat=application/json" - return geojson_url - - -def get_vector_layer_geoserver(state, district, block, specific_sheets=None): - print(f"Generate Stats excel for {state}_{district}_{block}") - base_path = os.path.join(EXCEL_PATH, "data/stats_excel_files") - district_path = os.path.join( - base_path, state.replace(" ", "_").upper(), district.replace(" ", "_").upper() - ) - os.makedirs(district_path, exist_ok=True) - xlsx_file = os.path.join(district_path, f"{district}_{block}.xlsx") - - workspaces_to_process = specific_sheets - - # Handle existing sheets when adding specific workspaces - results = [] - file_exists = os.path.exists(xlsx_file) - mode = "a" if file_exists else "w" - - # Use append mode with if_sheet_exists='replace' - with pd.ExcelWriter( - xlsx_file, - engine="openpyxl", - mode=mode, - if_sheet_exists="replace" if mode == "a" else None, - ) as writer: - for layer in fetch_layers_for_excel_generation(): - workspace = layer["workspace"] - - if workspaces_to_process and workspace not in workspaces_to_process: - continue - - start_year = layer.get("start_year") - end_year = layer.get("end_year") - - if "{district}" in layer["layer_name"] and "{block}" in layer["layer_name"]: - layer_name = layer["layer_name"].format(district=district, block=block) - else: - layer_name = layer["layer_name"] - - print(f"Processing layer: {layer_name}") - print(f"Workspace for the layer is: {workspace}") - - geojson_data = None - try: - url = get_url(workspace, layer_name) - response = requests.get(url) - response.raise_for_status() - geojson_data = response.json() - except requests.exceptions.RequestException as e: - print(f"Failed to fetch data for {layer_name}: {e}") - results.append( - {"layer": layer_name, "status": "failed", "workspace": workspace} - ) - continue - - # Process the data based on workspace - if workspace == "terrain": - create_excel_for_terrain(geojson_data, xlsx_file, writer) - elif ( - workspace == "terrain_lulc" - and layer_name == f"{district}_{block}_lulc_slope" - ): - create_excel_for_terrain_lulc_slope(geojson_data, xlsx_file, writer) - elif ( - workspace == "terrain_lulc" - and layer_name == f"{district}_{block}_lulc_plain" - ): - create_excel_for_terrain_lulc_plain(geojson_data, xlsx_file, writer) - elif workspace == "swb": - create_excel_for_swb( - geojson_data, xlsx_file, writer, start_year, end_year - ) - create_excel_for_mws_intersect_swb( - geojson_data, writer, district, block - ) - elif workspace == "nrega_assets": - mws_lay_name = f"deltaG_well_depth_{district}_{block}" - mws_file_url = get_url("mws_layers", mws_lay_name) - - try: - response = requests.get(mws_file_url) - response.raise_for_status() - mws_geojson_datas = response.json() - except requests.exceptions.RequestException as e: - print(f"Failed to fetch MWS data: {e}") - continue - - create_excel_for_nrega_assets( - geojson_data, - mws_geojson_datas, - xlsx_file, - writer, - start_year, - end_year, - ) - try: - fetch_village_asset_count( - state, district, block, writer, xlsx_file, start_year, end_year - ) - except Exception as e: - print("Exception as e", str(e)) - - elif workspace == "crop_intensity": - create_excel_crop_inten( - geojson_data, xlsx_file, writer, start_year, end_year - ) - elif workspace == "drought": - create_excel_crop_drou( - geojson_data, xlsx_file, writer, start_year, end_year - ) - elif ( - workspace == "mws_layers" - and layer_name == f"deltaG_well_depth_{district}_{block}" - ): - parsed_data_annual_mws = parse_geojson_annual_mws(geojson_data) - create_excel_annual_mws(parsed_data_annual_mws, xlsx_file, writer) - try: - create_excel_mws_inters_villages( - geojson_data, xlsx_file, writer, district, block - ) - except Exception as e: - print("Exception", str(e)) - elif ( - workspace == "mws_layers" - and layer_name == f"deltaG_fortnight_{district}_{block}" - ): - processed_data = [ - process_feature(feature) for feature in geojson_data["features"] - ] - create_excel_seas_mws( - processed_data, xlsx_file, writer, start_year, end_year - ) - elif workspace == "panchayat_boundaries": - create_excel_for_village_boun(geojson_data, writer) - elif workspace == "drought_causality": - create_excel_for_drought_causality( - geojson_data, xlsx_file, writer, start_year, end_year - ) - elif workspace == "ccd": - create_excel_for_ccd( - geojson_data, xlsx_file, writer, start_year, end_year - ) - elif workspace == "canopy_height": - create_excel_for_ch( - geojson_data, xlsx_file, writer, start_year, end_year - ) - elif workspace == "tree_overall_ch": - create_excel_for_overall_tree_change(geojson_data, xlsx_file, writer) - elif ( - workspace == "change_detection" - and layer_name == f"change_vector_{district}_{block}_Afforestation" - ): - create_excel_chan_detection_afforestation( - geojson_data, xlsx_file, writer - ) - elif ( - workspace == "change_detection" - and layer_name == f"change_vector_{district}_{block}_CropIntensity" - ): - create_excel_chan_detection_cropintensity( - geojson_data, xlsx_file, writer - ) - elif ( - workspace == "change_detection" - and layer_name == f"change_vector_{district}_{block}_Deforestation" - ): - create_excel_chan_detection_deforestation( - geojson_data, xlsx_file, writer - ) - elif ( - workspace == "change_detection" - and layer_name == f"change_vector_{district}_{block}_Degradation" - ): - create_excel_chan_detection_degradation(geojson_data, xlsx_file, writer) - elif ( - workspace == "change_detection" - and layer_name == f"change_vector_{district}_{block}_Urbanization" - ): - create_excel_chan_detection_urbanization( - geojson_data, xlsx_file, writer - ) - elif workspace == "restoration": - create_excel_for_restoration(geojson_data, xlsx_file, writer) - elif workspace == "aquifer": - create_excel_for_aquifer(geojson_data, writer) - elif workspace == "soge": - create_excel_for_soge(geojson_data, xlsx_file, writer) - elif workspace == "lcw": - create_excel_for_lcw(geojson_data, writer) - elif workspace == "agroecological": - create_excel_for_agroecological(geojson_data, writer) - elif workspace == "factory_csr": - create_excel_for_factory_csr(geojson_data, writer) - elif workspace == "green_credit": - create_excel_for_green_credit(geojson_data, writer) - elif workspace == "mining": - create_excel_for_mining(geojson_data, writer) - elif workspace == "stream_order": - create_excel_for_stream_order(geojson_data, writer) - elif workspace == "mws_connectivity": - create_excel_for_mws_connectivity(geojson_data, writer) - elif workspace == "mws": - create_excel_for_mws(geojson_data, writer) - elif workspace == "facilities_proximity": - create_excel_for_facilities(geojson_data, writer) - elif workspace == "dem": - create_excel_for_dem(geojson_data, writer) - elif workspace == "canal": - create_excel_for_canal(geojson_data, writer) - elif workspace == "river": - create_excel_for_river(geojson_data, writer) - elif workspace == "lulc_vector": - create_excel_for_lulc_vector(geojson_data, writer, start_year, end_year) - elif workspace == "drainage_density": - create_excel_for_drainage_density(geojson_data, writer) - elif workspace == "antyodaya_analysis": - create_excel_for_antyodaya_20(geojson_data, writer) - - results.append( - {"layer": layer_name, "status": "success", "workspace": workspace} - ) - - return results - - -def create_excel_for_antyodaya_20(data, writer): - features = data.get("features", []) - df_data = [feature.get("properties", {}) for feature in features] - df = pd.DataFrame(df_data) - - # Keep important columns first if they exist - first_cols = [c for c in ["village_id", "village_name"] if c in df.columns] - other_cols = [c for c in df.columns if c not in first_cols] - df = df[first_cols + other_cols] - - # Round numeric columns - numeric_cols = df.select_dtypes(include=["number"]).columns - df[numeric_cols] = df[numeric_cols].round(2) - df.to_excel(writer, sheet_name="antyodaya", index=False) - print("Excel file created for antyodaya") - - -def create_excel_for_drainage_density(data, writer): - print("Inside create_excel_for Drainage Density") - df_data = [] - features = data["features"] - - for feature in features: - properties = feature["properties"] - row = { - "UID": properties.get("uid", ""), - "area_in_ha": properties.get("area_in_ha", ""), - "drainage_density": properties.get("drainage_density", ""), - } - - df_data.append(row) - - df = pd.DataFrame(df_data) - df = df.sort_values(["UID"]) - numeric_cols = df.select_dtypes(include=["int64", "float64"]).columns - df[numeric_cols] = df[numeric_cols].round(2) - df.to_excel(writer, sheet_name="drainage_density", index=False) - print("Excel file created for Drainage Density") - - -def create_excel_for_lulc_vector(data, writer, start_year, end_year): - df_data = [] - features = data["features"] - years = list(range(start_year, end_year + 1)) - - classes = { - "barrenland": ("barrenland", "barrenla"), - "built_up_area": ("built-up_a", "built-up"), - "cropland": ("cropland_a", "cropland"), - "double_crop": ("doubly_cro", "doubly_c"), - "triple_crop": ("triply_cro", "triply_c"), - "tree_forest": ("tree_fores", "tree_for"), - "shrub_scrub": ("shrub_scru", "shrub_sc"), - "single_kharif": ("single_kha", "single_k"), - "single_non_kharif": ("single_non", "single_n"), - "k_water": ("k_water_ar", "k_water_"), - "kr_water": ("kr_water_a", "kr_water"), - "krz_water": ("krz_water_", "krz_wate"), - } - - def get_key(base_key, trunc_prefix, idx): - """Derive the property key for a given year index.""" - if idx == 0: - return base_key - return f"{trunc_prefix}_{idx}" - - for feature in features: - properties = feature["properties"] - - row = { - "UID": properties.get("uid", ""), - "area_in_ha": properties.get("area_in_ha", ""), - "sum_in_ha": (properties.get("sum") or 0) / 10000, - } - - for idx, year in enumerate(years): - for class_name, (base_key, trunc_prefix) in classes.items(): - key = get_key(base_key, trunc_prefix, idx) - row[f"{class_name}_in_ha_{year}"] = properties.get(key, 0) - - df_data.append(row) - - df = pd.DataFrame(df_data) - df = df.sort_values(["UID"]) - numeric_cols = df.select_dtypes(include=["int64", "float64"]).columns - df[numeric_cols] = df[numeric_cols].round(2) - df.to_excel(writer, sheet_name="lulc_vector", index=False) - print("Excel file created for lulc vector") - - -def create_excel_for_canal(data, writer): - print("Inside create_excel_for Canal") - df_data = [] - features = data["features"] - - for feature in features: - properties = feature["properties"] - row = { - "UID": properties.get("uid", ""), - "project_name": properties.get("prjname", ""), - "canal_code": properties.get("cancode", ""), - "canal_name": properties.get("canname", ""), - } - - df_data.append(row) - - df = pd.DataFrame(df_data) - df = df.sort_values(["UID"]) - df.to_excel(writer, sheet_name="canal", index=False) - print("Excel file created for canal") - - -def create_excel_for_river(data, writer): - print("Inside create_excel_for River") - df_data = [] - features = data["features"] - - for feature in features: - properties = feature["properties"] - row = { - "UID": properties.get("uid", ""), - "river_name": properties.get("rivname", ""), - } - - df_data.append(row) - - df = pd.DataFrame(df_data) - df = df.sort_values(["UID"]) - df.to_excel(writer, sheet_name="river", index=False) - print("Excel file created for river") - - -def create_excel_for_dem(data, writer): - print("Inside create_excel_for DEM") - - df_data = [] - features = data["features"] - - for feature in features: - properties = feature["properties"] - - row = { - "UID": properties.get("uid", ""), - "min_elevation": properties.get("min_elevation", ""), - "max_elevation": properties.get("max_elevation", ""), - "mean_elevation": properties.get("mean_elevation", ""), - } - - df_data.append(row) - - df = pd.DataFrame(df_data) - df = df.sort_values(["UID"]) - numeric_cols = df.select_dtypes(include=["int64", "float64"]).columns - df[numeric_cols] = df[numeric_cols].round(2) - df.to_excel(writer, sheet_name="dem", index=False) - print("Excel file created for dem") - - -def create_excel_for_mws_intersect_swb(swb_geojson, writer, district, block): - print("Inside create_excel_for_mws_intersect_swb") - - # --- Fetch MWS layer --- - mws_layer_name = f"mws_{district}_{block}" - mws_data_url = get_url("mws", mws_layer_name) - - mws_response = requests.get(mws_data_url) - if mws_response.status_code != 200: - print(f"Error fetching MWS data: {mws_response.status_code}") - return - - mws_geojson = mws_response.json() - - def calculate_intersection_area(geom1, geom2): - if geom1.intersects(geom2): - return geom1.intersection(geom2).area - return 0 - - rows = [] - - for mws_feature in mws_geojson["features"]: - mws_props = mws_feature["properties"] - mws_uid = mws_props.get("uid") - mws_geom = shape(mws_feature["geometry"]) - - for swb_feature in swb_geojson["features"]: - swb_props = swb_feature["properties"] - swb_geom = shape(swb_feature["geometry"]) - - intersection_area = calculate_intersection_area(mws_geom, swb_geom) - - if intersection_area > 0: - # waterbodies centroid calculation - centroid = swb_geom.centroid - lon, lat = centroid.x, centroid.y - - rows.append( - { - "UID": mws_uid, - "SWB_UID": swb_props.get("UID"), - "Waterbodies_name": swb_props.get("water_body_name"), - "Latitude": lat, - "Longitude": lon, - } - ) - - df = pd.DataFrame(rows) - - if not df.empty: - numeric_cols = df.select_dtypes(include=["int64", "float64"]).columns - df[numeric_cols] = df[numeric_cols].round(2) - - df.to_excel(writer, sheet_name="mws_intersect_swb", index=False) - print("Excel sheet 'mws_intersect_swb' created successfully") - - -def create_excel_for_facilities(data, writer): - features = data["features"] - df_data = [feature["properties"] for feature in features] - - df = pd.DataFrame(df_data) - - # keep first columns - first_cols = ["censuscode2011", "censusname"] - other_cols = [c for c in df.columns if c not in first_cols] - df = df[first_cols + other_cols] - numeric_cols = df.select_dtypes(include=["int64", "float64"]).columns - df[numeric_cols] = df[numeric_cols].round(2) - df.to_excel(writer, sheet_name="facilities_proximity", index=False) - print("Excel file created for facilities_proximity") - - -def create_excel_for_mws(data, writer): - df_data = [] - features = data["features"] - - for feature in features: - properties = feature["properties"] - row = { - "UID": properties.get("uid", ""), - "area_in_ha": properties.get("area_in_ha", ""), - "watershed_code": properties.get("wsconc", ""), - "basin_code": properties.get("bacode", ""), - "sub_basin_code": properties.get("sbcode", ""), - } - - df_data.append(row) - - df = pd.DataFrame(df_data) - df = df.sort_values(["UID"]) - df.to_excel(writer, sheet_name="mws", index=False) - print("Excel file created for mws") - - -def create_excel_for_mws_connectivity(data, writer): - """ - direction_map = { - '0': 'No Direction', - '1': 'North-East', - '2': 'East', - '3': 'South-East', - '4': 'South', - '5': 'South-West', - '6': 'West', - '7': 'North-West', - '8': 'North' - } - """ - df_data = [] - features = data["features"] - - for feature in features: - properties = feature["properties"] - row = { - "UID": properties["uid"], - "direction": properties["direction"], - "downstream_mws": properties["downstream"], - "upstream_mws": properties["upstream"], - } - - df_data.append(row) - df = pd.DataFrame(df_data) - df.replace("", "unknown", inplace=True) - df = df.sort_values(["UID"]) - df.to_excel(writer, sheet_name="mws_connectivity", index=False) - print("Excel file created for mws_connectivity") - - -def create_excel_for_stream_order(data, writer): - df_data = [] - features = data["features"] - - for feature in features: - properties = feature["properties"] - row = { - "UID": properties["uid"], - "order_1_area_percent": properties["1"], - "order_2_area_percent": properties["2"], - "order_3_area_percent": properties["3"], - "order_4_area_percent": properties["4"], - "order_5_area_percent": properties["5"], - "order_6_area_percent": properties["6"], - "order_7_area_percent": properties["7"], - "order_8_area_percent": properties["8"], - "order_9_area_percent": properties["9"], - "order_10_area_percent": properties["10"], - "order_11_area_percent": properties["11"], - } - - df_data.append(row) - df = pd.DataFrame(df_data) - df.replace("", "unknown", inplace=True) - df = df.sort_values(["UID"]) - df.to_excel(writer, sheet_name="stream_order", index=False) - print("Excel file created for stream order") - - -def create_excel_for_mining(data, writer): - df_data = [] - features = data["features"] - - for feature in features: - properties = feature["properties"] - row = { - "UID": properties["uid"], - "division": properties["company_na"], - "proposal": properties["proposal"], - "sector_moefcc": properties["sector_moe"], - "village": properties["village"], - } - - df_data.append(row) - df = pd.DataFrame(df_data) - df.replace("", "unknown", inplace=True) - df = df.sort_values(["UID"]) - df.to_excel(writer, sheet_name="mining", index=False) - print("Excel file created for mining") - - -def create_excel_for_green_credit(data, writer): - df_data = [] - features = data["features"] - - for feature in features: - properties = feature["properties"] - row = { - "UID": properties["uid"], - "division": properties["division"], - # "parcel_id": properties["parcel_id"], - "land_info": properties["land_info"], - "kml_url": properties["kml_url"], - } - - df_data.append(row) - df = pd.DataFrame(df_data) - df = df.sort_values(["UID"]) - df.to_excel(writer, sheet_name="green_credit", index=False) - print("Excel file created for green_credit") - - -def create_excel_for_factory_csr(data, writer): - df_data = [] - features = data["features"] - - for feature in features: - properties = feature["properties"] - row = { - "UID": properties["uid"], - "Company_Name": properties["COMPANY NA"], - "ADDRESS": properties["ADDRESS"], - "LOCATION T": properties["LOCATION T"], - } - - df_data.append(row) - df = pd.DataFrame(df_data) - df = df.sort_values(["UID"]) - df.to_excel(writer, sheet_name="factory_csr", index=False) - print("Excel file created for factory_csr") - - -def create_excel_for_agroecological(data, writer): - df_data = [] - features = data["features"] - - for feature in features: - properties = feature["properties"] - row = { - "UID": properties["uid"], - "organization_name": properties["organization_name"], - "organization_type": properties["organization_type"], - "created_at": properties["created_at"], - "contact_person": properties["contact_person"], - "email": properties["email"], - "domains": properties["domains"], - } - - df_data.append(row) - df = pd.DataFrame(df_data) - df = df.sort_values(["UID"]) - df.to_excel(writer, sheet_name="agroecological", index=False) - print("Excel file created for agroecological") - - -def create_excel_for_lcw(data, writer): - df_data = [] - features = data["features"] - - for feature in features: - properties = feature["properties"] - row = { - "UID": properties["uid"], - "title_of_conflict": properties["Title of Conflict"], - "link_to_conflict": properties["Link to conflict"], - } - - df_data.append(row) - df = pd.DataFrame(df_data) - df = df.sort_values(["UID"]) - df.to_excel(writer, sheet_name="lcw_conflict", index=False) - print("Excel file created for lcw_conflict") - - -def create_excel_for_soge(data, xlsx_file, writer): - df_data = [] - features = data["features"] - - for feature in features: - properties = feature["properties"] - row = { - "UID": properties["uid"], - "area_in_ha": properties["area_in_ha"], - "soge_dev_percent": properties["sgw_dev_pe"], - "class_code": properties["code"], - "class_name": properties["class"], - } - - df_data.append(row) - df = pd.DataFrame(df_data) - df = df.sort_values(["UID"]) - - ## for roundoff all numeric value upto 2 decimal - numeric_cols = df.select_dtypes(include=["int64", "float64"]).columns - df[numeric_cols] = df[numeric_cols].round(2) - - df.to_excel(writer, sheet_name="soge_vector", index=False) - print(f"Excel file created for soge_vector") - - -def create_excel_for_aquifer(data, writer): - df_data = [] - features = data["features"] - - for feature in features: - properties = feature["properties"] - if properties.get("aquifer_class", "") == "Hard-Rock": - aquifer_class = "Hard Rock" - else: - aquifer_class = "Alluvium" - row = { - "UID": properties.get("uid", ""), - "area_in_ha": properties.get("area_in_ha", ""), - "aquifer_class": aquifer_class, - "principle_aq_Alluvium_percent": properties.get( - "principle_aq_Alluvium_percent", "0" - ), - "principle_aq_Banded Gneissic Complex_percent": properties.get( - "principle_aq_Banded Gneissic Complex_percent", "0" - ), - "principle_aq_Basalt_percent": properties.get( - "principle_aq_Basalt_percent", "0" - ), - "principle_aq_Charnockite_percent": properties.get( - "principle_aq_Charnockite_percent", "0" - ), - "principle_aq_Gneiss_percent": properties.get( - "principle_aq_Gneiss_percent", "0" - ), - "principle_aq_Granite_percent": properties.get( - "principle_aq_Granite_percent", "0" - ), - "principle_aq_Intrusive_percent": properties.get( - "principle_aq_Intrusive_percent", "0" - ), - "principle_aq_Khondalite_percent": properties.get( - "principle_aq_Khondalite_percent", "0" - ), - "principle_aq_Laterite_percent": properties.get( - "principle_aq_Laterite_percent", "0" - ), - "principle_aq_Limestone_percent": properties.get( - "principle_aq_Limestone_percent", "0" - ), - "principle_aq_Quartzite_percent": properties.get( - "principle_aq_Quartzite_percent", "0" - ), - "principle_aq_Sandstone_percent": properties.get( - "principle_aq_Sandstone_percent", "0" - ), - "principle_aq_Schist_percent": properties.get( - "principle_aq_Schist_percent", "0" - ), - "principle_aq_Shale_percent": properties.get( - "principle_aq_Shale_percent", "0" - ), - "principle_aq_None_percent": properties.get( - "principle_aq_None_percent", "0" - ), - } - - df_data.append(row) - - df = pd.DataFrame(df_data) - # Round numeric values - numeric_cols = df.select_dtypes(include=["number"]).columns - df[numeric_cols] = df[numeric_cols].round(2) - df = df.sort_values(["UID"]) - df.to_excel(writer, sheet_name="aquifer_vector", index=False) - print("Excel file created for aquifer_vector") - - -def create_excel_for_restoration(data, xlsx_file, writer): - df_data = [] - features = data["features"] - - for feature in features: - properties = feature["properties"] - row = { - "UID": properties["uid"], - "area_in_ha": properties["area_in_ha"], - "wide_scale_restoration_area_in_ha": properties["Wide-scale"], - "protection_area_in_ha": properties["Protection"], - "mosaic_restoration_area_in_ha": properties["Mosaic Res"], - "excluded_areas_in_ha": properties["Excluded A"], - } - - df_data.append(row) - df = pd.DataFrame(df_data) - df = df.sort_values(["UID"]) - - ## for roundoff all numeric value upto 2 decimal - numeric_cols = df.select_dtypes(include=["int64", "float64"]).columns - df[numeric_cols] = df[numeric_cols].round(2) - - df.to_excel(writer, sheet_name="restoration_vector", index=False) - print(f"Excel file created for restoration_vector") - - -def create_excel_for_overall_tree_change(data, xlsx_file, writer): - df_data = [] - features = data["features"] - - for feature in features: - properties = feature["properties"] - row = { - "UID": properties["uid"], - "area_in_ha": properties["area_in_ha"], - "afforestation_area_in_ha": properties["Afforestation"], - "deforestation_area_in_ha": properties["Deforestation"], - "degradation_area_in_ha": properties["Degradation"], - "improvement_area_in_ha": properties["Improvement"], - "missing_data_in_ha": properties["Missing Data"], - "no_change_area_in_ha": properties["No_Change"], - "partially_degraded_area_in_ha": properties["Partially_Degraded"], - } - - df_data.append(row) - df = pd.DataFrame(df_data) - df = df.sort_values(["UID"]) - - ## for roundoff all numeric value upto 2 decimal - numeric_cols = df.select_dtypes(include=["int64", "float64"]).columns - df[numeric_cols] = df[numeric_cols].round(2) - - df.to_excel(writer, sheet_name="overall_tree_change", index=False) - print(f"Excel file created for overall_tree_change") - - -def create_excel_for_ccd(data, xlsx_file, writer, start_year, end_year): - df_data = [] - features = data["features"] - for feature in features: - properties = feature["properties"] - - row = { - "UID": properties["uid"], - "area_in_ha": properties["area_in_ha"], - } - - for year in range(start_year, end_year + 1): - row["high_density_area_in_ha_" + str(year)] = properties.get( - "High_Density_" + str(year), None - ) - row["low_density_area_in_ha_" + str(year)] = properties.get( - "Low_Density_" + str(year), None - ) - row["missing_data_area_in_ha_" + str(year)] = properties.get( - "Missing_Data_" + str(year), None - ) - - df_data.append(row) - - df = pd.DataFrame(df_data) - df = df.sort_values(["UID"]) - - ## for roundoff all numeric value upto 2 decimal - numeric_cols = df.select_dtypes(include=["int64", "float64"]).columns - df[numeric_cols] = df[numeric_cols].round(2) - - df.to_excel(writer, sheet_name="Canopy_Cover_Density", index=False) - print(f"Excel file created for Canopy_Cover_Density") - - -def create_excel_for_ch(data, xlsx_file, writer, start_year, end_year): - df_data = [] - features = data["features"] - for feature in features: - properties = feature["properties"] - - row = { - "UID": properties["uid"], - "area_in_ha": properties["area_in_ha"], - } - - for year in range(start_year, end_year + 1): - row["short_trees_area_in_ha_" + str(year)] = properties.get( - "Short_Trees_" + str(year), None - ) - row["medium_trees_area_in_ha_" + str(year)] = properties.get( - "Medium_Height_Trees_" + str(year), None - ) - row["tall_trees_area_in_ha_" + str(year)] = properties.get( - "Tall_Trees_" + str(year), None - ) - row["missing_data_area_in_ha_" + str(year)] = properties.get( - "Missing_Data_" + str(year), None - ) - - df_data.append(row) - - df = pd.DataFrame(df_data) - df = df.sort_values(["UID"]) - - ## for roundoff all numeric value upto 2 decimal - numeric_cols = df.select_dtypes(include=["int64", "float64"]).columns - df[numeric_cols] = df[numeric_cols].round(2) - - df.to_excel(writer, sheet_name="Canopy_height", index=False) - print(f"Excel file created for Canopy_height") - - -def create_excel_for_drought_causality(data, xlsx_file, writer, start_year, end_year): - df_data = [] - features = data["features"] - for feature in features: - properties = feature["properties"] - - row = { - "UID": properties["uid"], - "area_in_ha": properties["area_in_ha"], - } - - for year in range(start_year, end_year + 1): - row["severe_moderate_drought_causality_" + str(year)] = properties.get( - "se_mo_" + str(year), None - ) - row["mild_drought_causality_" + str(year)] = properties.get( - "mild_" + str(year), None - ) - - df_data.append(row) - - df = pd.DataFrame(df_data) - df = df.sort_values(["UID"]) - - ## for roundoff all numeric value upto 2 decimal - numeric_cols = df.select_dtypes(include=["int64", "float64"]).columns - df[numeric_cols] = df[numeric_cols].round(2) - - df.to_excel(writer, sheet_name="drought_causality", index=False) - print(f"Excel file created for drought_causality") - - -def create_excel_chan_detection_afforestation(data, xlsx_file, writer): - df_data = [] - features = data["features"] - - for feature in features: - properties = feature["properties"] - uid = properties.get("uid", "Unknown") - df_data.append( - { - "UID": uid, - "area_in_ha": properties.get("area_in_ha", None), - "barren_to_forest_area_in_ha": properties.get("ba_fo", None), - "built_up_to_forest_area_in_ha": properties.get("bu_fo", None), - "farm_to_forest_area_in_ha": properties.get("fa_fo", None), - "forest_to_forest_area_in_ha": properties.get("fo_fo", None), - "scrub_land_to_forest_area_in_ha": properties.get("sc_fo", None), - "total_afforestation_area_in_ha": properties.get("total_aff", None), - } - ) - - df = pd.DataFrame(df_data) - df = df.sort_values(["UID"]) - - ## for roundoff all numeric value upto 2 decimal - numeric_cols = df.select_dtypes(include=["int64", "float64"]).columns - df[numeric_cols] = df[numeric_cols].round(2) - - df.to_excel(writer, sheet_name="change_detection_afforestation", index=False) - print(f"Excel file created for change_detection_afforestation") - - -def create_excel_chan_detection_cropintensity(data, xlsx_file, writer): - df_data = [] - features = data["features"] - - for feature in features: - properties = feature["properties"] - uid = properties.get("uid", "Unknown") - df_data.append( - { - "UID": uid, - "area_in_ha": properties.get("area_in_ha", None), - "single_to_single_area_in_ha": properties.get("si_si", None), - "single_to_double_area_in_ha": properties.get("si_do", None), - "single_to_triple_area_in_ha": properties.get("si_tr", None), - "double_to_single_area_in_ha": properties.get("do_si", None), - "double_to_double_area_in_ha": properties.get("do_do", None), - "double_to_triple_area_in_ha": properties.get("do_tr", None), - "triple_to_single_area_in_ha": properties.get("tr_si", None), - "triple_to_double_area_in_ha": properties.get("tr_do", None), - "triple_to_triple_area_in_ha": properties.get("tr_tr", None), - "total_change_crop_intensity_area_in_ha": properties.get( - "total_chan", properties.get("total_change", None) - ), - } - ) - - df = pd.DataFrame(df_data) - df = df.sort_values(["UID"]) - - ## for roundoff all numeric value upto 2 decimal - numeric_cols = df.select_dtypes(include=["int64", "float64"]).columns - df[numeric_cols] = df[numeric_cols].round(2) - - df.to_excel(writer, sheet_name="change_detection_cropintensity", index=False) - print(f"Excel file created for change_detection_cropintensity") - - -def create_excel_chan_detection_deforestation(data, xlsx_file, writer): - df_data = [] - features = data["features"] - - for feature in features: - properties = feature["properties"] - uid = properties.get("uid", "Unknown") - df_data.append( - { - "UID": uid, - "area_in_ha": properties.get("area_in_ha", None), - "forest_to_barren_area_in_ha": properties.get("fo_ba", None), - "forest_to_built_up_area_in_ha": properties.get("fo_bu", None), - "forest_to_farm_area_in_ha": properties.get("fo_fa", None), - "forest_to_forest_area_in_ha": properties.get("fo_fo", None), - "forest_to_scrub_land_area_in_ha": properties.get("fo_sc", None), - "total_deforestation_area_in_ha": properties.get("total_def", None), - } - ) - - df = pd.DataFrame(df_data) - df = df.sort_values(["UID"]) - - ## for roundoff all numeric value upto 2 decimal - numeric_cols = df.select_dtypes(include=["int64", "float64"]).columns - df[numeric_cols] = df[numeric_cols].round(2) - - df.to_excel(writer, sheet_name="change_detection_deforestation", index=False) - print(f"Excel file created for change_detection_deforestation") - - -def create_excel_chan_detection_degradation(data, xlsx_file, writer): - df_data = [] - features = data["features"] - - for feature in features: - properties = feature["properties"] - uid = properties.get("uid", "Unknown") - df_data.append( - { - "UID": uid, - "area_in_ha": properties.get("area_in_ha", None), - "farm_to_barren_area_in_ha": properties.get("f_ba", None), - "farm_to_built_up_area_in_ha": properties.get("f_bu", None), - "farm_to_farm_area_in_ha": properties.get("f_f", None), - "farm_to_scrub_land_area_in_ha": properties.get("f_sc", None), - "total_degradation_area_in_ha": properties.get("total_deg", None), - } - ) - - df = pd.DataFrame(df_data) - df = df.sort_values(["UID"]) - - ## for roundoff all numeric value upto 2 decimal - numeric_cols = df.select_dtypes(include=["int64", "float64"]).columns - df[numeric_cols] = df[numeric_cols].round(2) - - df.to_excel(writer, sheet_name="change_detection_degradation", index=False) - print(f"Excel file created for change_detection_degradation") - - -def create_excel_chan_detection_urbanization(data, xlsx_file, writer): - df_data = [] - features = data["features"] - - for feature in features: - properties = feature["properties"] - uid = properties.get("uid", "Unknown") - df_data.append( - { - "UID": uid, - "area_in_ha": properties.get("area_in_ha", None), - "barren_shrub_to_built_up_area_in_ha": properties.get("b_bu", None), - "built_up_to_built_up_area_in_ha": properties.get("bu_bu", None), - "tree_farm_to_built_up_area_in_ha": properties.get("tr_bu", None), - "water_to_built_up_area_in_ha": properties.get("w_bu", None), - "total_urbanization_area_in_ha": properties.get("total_urb", None), - } - ) - - df = pd.DataFrame(df_data) - df = df.sort_values(["UID"]) - - ## for roundoff all numeric value upto 2 decimal - numeric_cols = df.select_dtypes(include=["int64", "float64"]).columns - df[numeric_cols] = df[numeric_cols].round(2) - - df.to_excel(writer, sheet_name="change_detection_urbanization", index=False) - print(f"Excel file created for change_detection_urbanization") - - -def create_excel_mws_inters_villages(mws_geojson, xlsx_file, writer, district, block): - print("Inside create_excel_mws_inters_villages") - admin_layer_name = district + "_" + block - admin_file_url = get_url("panchayat_boundaries", admin_layer_name) - - response = requests.get(admin_file_url) - if response.status_code != 200: - print(f"Error fetching data: {response.status_code}") - return - - village_geojson = response.json() - - def calculate_intersection_area_ha(village_geom, mws_geom, mws_area_ha): - if village_geom.intersects(mws_geom): - intersection = village_geom.intersection(mws_geom) - if mws_geom.area == 0: - return 0 - # ratio of intersection to full MWS geometry area (both in degrees²) - ratio = intersection.area / mws_geom.area - return ratio * mws_area_ha - return 0 - - mws_villages_dict = {} - - for mws_feature in mws_geojson["features"]: - mws_uid = mws_feature["properties"]["uid"] - mws_area = mws_feature["properties"]["area_in_ha"] - mws_geom = shape(mws_feature["geometry"]) - village_ids = set() - village_details = {} - - for village_feature in village_geojson["features"]: - village_id = village_feature["properties"]["vill_ID"] - if village_id == 0: - continue - - village_geom = shape(village_feature["geometry"]) - area_intersected_ha = calculate_intersection_area_ha( - village_geom, mws_geom, mws_area - ) - if area_intersected_ha > 0: - village_ids.add(village_id) - percentage_of_area = ( - (area_intersected_ha / mws_area) * 100 if mws_area > 0 else 0 - ) - village_details[village_id] = { - "area_intersect": round(area_intersected_ha, 2), - "percentage_of_area": round(percentage_of_area, 2), - } - - if village_ids: - mws_villages_dict[mws_uid] = { - "area_in_ha": mws_area, - "village_ids": list(village_ids), - "village_details": village_details, - } - - data = [ - { - "MWS UID": mws_uid, - "area_in_ha": values["area_in_ha"], - "Village IDs": values["village_ids"], - "Village Details": values["village_details"], - } - for mws_uid, values in mws_villages_dict.items() - ] - - df = pd.DataFrame(data) - numeric_cols = df.select_dtypes(include=["int64", "float64"]).columns - df[numeric_cols] = df[numeric_cols].round(2) - df.to_excel(writer, sheet_name="mws_intersect_villages", index=False) - print("The data has been saved to mws_intersect_villages") - - -# def create_excel_village_inters_mwss(mws_geojson, xlsx_file, writer, district, block): -# print("Inside create_excel_village_inters_mwss") -# admin_layer_name = district + '_' + block -# admin_file_url = get_url('panchayat_boundaries', admin_layer_name) - -# response = requests.get(admin_file_url) -# if response.status_code != 200: -# print(f"Error fetching data: {response.status_code}") -# return -# village_geojson = response.json() - -# def calculate_intersection_area(village_geom: BaseGeometry, mws_geom: BaseGeometry) : -# try: -# # Check for empty geometries -# if village_geom.is_empty or mws_geom.is_empty: -# return 0.0 - -# # Fix invalid geometries if needed -# if not village_geom.is_valid: -# village_geom = village_geom.buffer(0) -# if not mws_geom.is_valid: -# mws_geom = mws_geom.buffer(0) - -# # Calculate intersection -# if village_geom.intersects(mws_geom): -# intersection = village_geom.intersection(mws_geom) -# return intersection.area if not intersection.is_empty else 0.0 - -# return 0.0 - -# except Exception as e: -# print(f"Error calculating intersection area: {e}") -# return 0.0 - - -# data = [] - -# processed_villages = set() - -# for village_feature in village_geojson['features']: -# village_id = village_feature['properties']['vill_ID'] -# village_name = village_feature['properties']['vill_name'] - -# village_key = (village_id, village_name) - -# if village_key in processed_villages: -# continue -# processed_villages.add(village_key) - -# village_geom = shape(village_feature['geometry']) - -# mws_uids = [] -# intersection_areas = [] - -# for mws_feature in mws_geojson['features']: -# mws_geom = shape(mws_feature['geometry']) -# area_intersected = calculate_intersection_area(village_geom, mws_geom) -# if area_intersected > 0: -# mws_uids.append(mws_feature['properties']['uid']) -# intersection_areas.append(area_intersected) - -# data.append({ -# 'Village ID': village_id, -# 'Village Name': village_name, -# 'MWS UIDs': mws_uids, -# }) - -# df = pd.DataFrame(data) - -# ## for roundoff all numeric value upto 2 decimal -# numeric_cols = df.select_dtypes(include=['int64', 'float64']).columns -# df[numeric_cols] = df[numeric_cols].round(2) - -# df.to_excel(writer, sheet_name='village_intersect_mwss', index=False) -# print("Excel created for village_intersect_mwss") - - -def create_excel_for_terrain(data, output_file, writer): - print("Inside create_excel_for_terrain function") - df_data = [] - - terrain_description = { - 0: "Broad Sloppy and Hilly", - 1: "Mostly Plains", - 2: "Mostly Hills and Valleys", - 3: "Broad Plains and Slopes", - } - - features = data["features"] - - for feature in features: - properties = feature["properties"] - row = { - "UID": properties["uid"], - "area_in_ha": properties["area_in_ha"], - "terrain_cluster_id": properties["terrainClu"], - "terrain_description": terrain_description.get(properties["terrainClu"]), - "hill_slope_area_percent": properties["hill_slope"], - "plain_area_percent": properties["plain_area"], - "ridge_area_percent": properties["ridge_area"], - "slopy_area_percent": properties["slopy_area"], - "valley_area_percent": properties["valley_are"], - } - - df_data.append(row) - - df = pd.DataFrame(df_data) - df = df.sort_values(["UID"]) - - ## for roundoff all numeric value upto 2 decimal - numeric_cols = df.select_dtypes(include=["int64", "float64"]).columns - df[numeric_cols] = df[numeric_cols].round(2) - - df.to_excel(writer, sheet_name="terrain", index=False) - print(f"Excel file created for terrain vector") - - -def create_excel_for_terrain_lulc_slope(data, output_file, writer): - df_data = [] - - terrain_description = { - 0: "Broad Sloppy and Hilly", - 1: "Mostly Plains", - 2: "Mostly Hills and Valleys", - 3: "Broad Plains and Slopes", - } - - features = data["features"] - - for feature in features: - properties = feature["properties"] - row = { - "UID": properties["uid"], - "area_in_ha": properties["area_in_ha"], - "terrain_cluster_id": properties["terrain_cl"], - "terrain_description": terrain_description.get(properties["terrain_cl"]), - "cluster_name": properties["clust_name"], - "barren_area_percent": properties["barren"], - "forests_area_percent": properties["forests"], - "shrub_scrubs_area_percent": properties["shrub_scru"], - "single_kharif_area_percent": properties["sing_khari"], - "single_non_kharif_area_percent": properties["sing_non_k"], - "double_cropping_area_percent": properties["double"], - "triple_cropping_area_percent": properties["triple"], - } - - df_data.append(row) - df = pd.DataFrame(df_data) - df = df.sort_values(["UID"]) - - ## for roundoff all numeric value upto 2 decimal - numeric_cols = df.select_dtypes(include=["int64", "float64"]).columns - df[numeric_cols] = df[numeric_cols].round(2) - - df.to_excel(writer, sheet_name="terrain_lulc_slope", index=False) - print("Excel file created for terrain_lulc_slope") - - -def create_excel_for_terrain_lulc_plain(data, output_file, writer): - df_data = [] - - terrain_description = { - 0: "Broad Sloppy and Hilly", - 1: "Mostly Plains", - 2: "Mostly Hills and Valleys", - 3: "Broad Plains and Slopes", - } - - features = data["features"] - - for feature in features: - properties = feature["properties"] - row = { - "UID": properties["uid"], - "area_in_ha": properties["area_in_ha"], - "terrain_cluster_id": properties["terrain_cl"], - "terrain_description": terrain_description.get(properties["terrain_cl"]), - "cluster_name": properties["clust_name"], - "barren_area_percent": properties["barren"], - "forests_area_percent": properties["forest"], - "shrub_scrubs_area_percent": properties["shrubs_scr"], - "single_non_kharif_area_percent": properties["sing_non_k"], - "single_kharif_area_percent": properties["sing_crop"], - "double_cropping_area_percent": properties["double_cro"], - "triple_cropping_area_percent": properties["triple_cro"], - } - - df_data.append(row) - - df = pd.DataFrame(df_data) - df = df.sort_values(["UID"]) - - numeric_cols = df.select_dtypes(include=["int64", "float64"]).columns - df[numeric_cols] = df[numeric_cols].round(2) - - df.to_excel(writer, sheet_name="terrain_lulc_plain", index=False) - print("Excel file created for terrain_lulc_plain") - - -def create_excel_for_swb(data, output_file, writer, start_year, end_year): - df_data = [] - features = data.get("features", []) - - for feature in features: - properties = feature.get("properties", {}) - uid = properties.get("MWS_UID", "Unknown") - - def calculate_area(base_area, percentage): - if base_area == 0 or percentage == 0: - return 0 - return base_area * (percentage / 100) - - parts = uid.split("_") - num_uid_parts_is = [ - f"{parts[i]}_{parts[i+1]}" for i in range(0, len(parts) - 1, 2) - ] - if len(parts) % 2 == 1: # Check for an unpaired last part - num_uid_parts_is.append(parts[-1]) - - # Generate years dynamically based on start_year and end_year - years = range(start_year, end_year) - - for num_uid_part in num_uid_parts_is: - row = {"UID": num_uid_part} - - for year in years: - short_year = f"{str(year)[-2:]}-{str(year+1)[-2:]}" - - # Construct keys dynamically using the shortened year format - total_area_key = f"area_{short_year}" - kharif_key = f"k_{short_year}" - rabi_key = f"kr_{short_year}" - zaid_key = f"krz_{short_year}" - - # Get values from properties - total_area = properties.get(total_area_key, 0) - kharif_percentage = properties.get(kharif_key, 0) - rabi_percentage = properties.get(rabi_key, 0) - zaid_percentage = properties.get(zaid_key, 0) - - # Calculate areas - row[f"total_area_in_ha_{year}-{year+1}"] = total_area / len( - num_uid_parts_is - ) - row[f"kharif_area_in_ha_{year}-{year+1}"] = calculate_area( - total_area, kharif_percentage - ) / len(num_uid_parts_is) - row[f"rabi_area_in_ha_{year}-{year+1}"] = calculate_area( - total_area, rabi_percentage - ) / len(num_uid_parts_is) - row[f"zaid_area_in_ha_{year}-{year+1}"] = calculate_area( - total_area, zaid_percentage - ) / len(num_uid_parts_is) - - # Add total SWB area - row["total_swb_area_in_ha"] = properties.get("area_ored", 0) / len( - num_uid_parts_is - ) - df_data.append(row) - - df = pd.DataFrame(df_data) - agg_dict = {col: "sum" for col in df.columns if col != "UID"} - grouped_df = df.groupby("UID").agg(agg_dict).reset_index() - - df = grouped_df.sort_values(["UID"]) - - numeric_cols = df.select_dtypes(include=["int64", "float64"]).columns - df[numeric_cols] = df[numeric_cols].round(2) - - df.to_excel(writer, sheet_name="surfaceWaterBodies_annual", index=False) - print("Excel file created for surfaceWaterBodies_annual") - - -def create_excel_for_nrega_assets( - nrega_data, mws_data, output_file, writer, start_year, end_year -): - workCategoryMapping = { - "SWC - Landscape level impact": "Soil and water conservation", - "Agri Impact - HH, Community": "Land restoration", - "Plantation": "Plantations", - "Irrigation - Site level impact": "Irrigation on farms", - "Irrigation Site level - Non RWH": "Other farm works", - "Household Livelihood": "Off-farm livelihood assets", - "Others - HH, Community": "Community assets", - } - - mws = gpd.GeoDataFrame.from_features(mws_data["features"]) - nrega = gpd.GeoDataFrame.from_features(nrega_data["features"]) - - # Set CRS if available in JSON - if "crs" in mws_data: - mws.set_crs(mws_data["crs"]["properties"]["name"], inplace=True) - if "crs" in nrega_data: - nrega.set_crs(nrega_data["crs"]["properties"]["name"], inplace=True) - - joined = gpd.sjoin(nrega, mws, how="inner", predicate="within") - counts = {} - - df_data = [] - valid_years = range(start_year, end_year) - - date_formats = [ - "%d-%b-%y %H:%M:%S.%f", - "%d-%b-%y %H:%M:%S", - "%d-%m-%y %H:%M:%S.%f", - "%d-%m-%y %H:%M:%S", - "%d-%b-%Y %H:%M:%S.%f", - "%d-%b-%Y %H:%M:%S", - "%d-%m-%Y %H:%M:%S.%f", - "%d-%m-%Y %H:%M:%S", - "%Y-%m-%d %H:%M:%S.%f", - "%Y-%m-%d %H:%M:%S", - "%Y/%m/%d %H:%M:%S.%f", - "%Y/%m/%d %H:%M:%S", - "%Y-%m-%dT%H:%M:%SZ", - ] - - for _, row in joined.iterrows(): - creation_t = row["creation_t"] - work_category = row["WorkCatego"] - mws_id = row["uid"] - - if isinstance(creation_t, pd.Timestamp): - creation_t = creation_t.strftime("%d-%m-%Y %H:%M:%S") - - date_obj = None - for date_format in date_formats: - try: - date_obj = datetime.strptime(creation_t, date_format) - break - except ValueError: - continue - - if date_obj is None: - continue - - year = date_obj.year - if year < 100: - year += 2000 - - if year not in valid_years: - continue - - category = workCategoryMapping.get(work_category, "Others - HH, Community") - - if mws_id not in counts: - counts[mws_id] = { - year: {cat: 0 for cat in workCategoryMapping.values()} - for year in range(start_year, end_year) - } - - if category not in counts[mws_id][year]: - counts[mws_id][year][category] = 0 - counts[mws_id][year][category] += 1 - - for mws_id, year_data in counts.items(): - row = {"mws_id": mws_id} - for year, categories in year_data.items(): - for category in workCategoryMapping.values(): - count = categories.get(category, 0) - row[f"{category}_count_{year}"] = count - df_data.append(row) - - if not df_data: - print("No data was collected for the DataFrame.") - else: - print(f"Collected {len(df_data)} rows of data for the DataFrame.") - - if df_data: - df = pd.DataFrame(df_data) - df.to_excel(writer, sheet_name="nrega_annual", index=False) - print("Excel file created for nrega_annual") - return "successfully created" - else: - print("No data available to write to Excel.") - - -def create_excel_village_nrega_assets( - result_df, output_file, writer, all_villages_df, start_year, end_year -): - workCategoryMapping = { - "SWC - Landscape level impact": "Soil and water conservation", - "Agri Impact - HH, Community": "Land restoration", - "Plantation": "Plantations", - "Irrigation - Site level impact": "Irrigation on farms", - "Irrigation Site level - Non RWH": "Other farm works", - "Household Livelihood": "Off-farm livelihood assets", - "Others - HH, Community": "Community assets", - } - - # start_year, end_year = 2017, 2022 - year_range = range(start_year, end_year + 1) - - # Initialize all-zero DataFrame for all villages - rows = [] - for _, row in all_villages_df.iterrows(): - base_row = {"vill_id": row["vill_ID"], "vill_name": row["vill_name"]} - for year in year_range: - for cat in workCategoryMapping.values(): - base_row[f"{cat}_count_{year}"] = 0 - rows.append(base_row) - - final_df = pd.DataFrame(rows) - - # Fill counts from assets - for _, row in result_df.iterrows(): - creation_t = row["creation_t"] - try: - date_obj = pd.to_datetime(creation_t, errors="coerce") - if pd.isnull(date_obj): - continue - except: - continue - - year = date_obj.year - if year not in year_range: - continue - - category = workCategoryMapping.get(row["WorkCatego"]) - if not category: - continue - - mask = (final_df["vill_id"] == row["vill_ID"]) & ( - final_df["vill_name"] == row["vill_name"] - ) - col_name = f"{category}_count_{year}" - final_df.loc[mask, col_name] += 1 - - # Sort columns for clean layout - id_cols = ["vill_id", "vill_name"] - category_cols = sorted( - [col for col in final_df.columns if col not in id_cols], - key=lambda x: (int(x.split("_")[-1]), x), - ) - final_df = final_df[id_cols + category_cols] - - # Save to Excel - final_df = final_df.drop_duplicates(subset=["vill_id", "vill_name"]) - final_df.to_excel(writer, sheet_name="nrega_assets_village", index=False) - print("Excel file created successfully with all villages.") - - -def fetch_village_asset_count( - state, district, block, writer, output_file, start_year, end_year -): - # 1. Read village data - village_gdf = gpd.read_file(get_url("panchayat_boundaries", f"{district}_{block}"))[ - ["vill_ID", "vill_name", "geometry"] - ].copy() - print("Village data loaded") - - # 2. Get NREGA data with timeout to prevent hanging - try: - nrega_url = get_url("nrega_assets", f"{district}_{block}") - print(f"Fetching: {nrega_url}") - - # Add timeout to prevent hanging - response = requests.get(nrega_url, timeout=120) - nrega_json = response.json() - print("NREGA data fetched successfully") - - except Exception as e: - print(f"Failed to get NREGA data: {e}") - # Return villages with "No Asset" if API fails - result_df = village_gdf[["vill_ID", "vill_name"]].copy() - result_df["Asset ID"] = "No Asset" - result_df["creation_t"] = "No Asset" - result_df["WorkCatego"] = "No Asset" - create_excel_village_nrega_assets( - result_df, - output_file, - writer, - village_gdf[["vill_ID", "vill_name"]], - start_year, - end_year, - ) - return result_df, village_gdf[["vill_ID", "vill_name"]] - - # 3. Process asset features - points_data = [] - features = nrega_json.get("features", []) - print(f"Processing {len(features)} features") - - for feature in features: - try: - point = Point(feature["geometry"]["coordinates"]) - properties = feature["properties"] - points_data.append( - { - "geometry": point, - "Asset ID": properties.get("Asset ID", "MISSING"), - "creation_t": properties.get("creation_t", ""), - "WorkCatego": properties.get("WorkCatego", ""), - } - ) - except: - continue - - # If no valid points, return no assets - if not points_data: - print("No valid asset points found") - result_df = village_gdf[["vill_ID", "vill_name"]].copy() - result_df["Asset ID"] = "No Asset" - result_df["creation_t"] = "No Asset" - result_df["WorkCatego"] = "No Asset" - create_excel_village_nrega_assets( - result_df, - output_file, - writer, - village_gdf[["vill_ID", "vill_name"]], - start_year, - end_year, - ) - return result_df, village_gdf[["vill_ID", "vill_name"]] - - print("Asset points created") - - # 4. Create points GeoDataFrame and match CRS - points_gdf = gpd.GeoDataFrame(points_data, geometry="geometry") - if village_gdf.crs != points_gdf.crs: - points_gdf.set_crs(village_gdf.crs, inplace=True) - - # 5. Find which village each asset belongs to - joined_gdf = gpd.sjoin(points_gdf, village_gdf, how="inner", predicate="within") - - # 6. Get asset + village info - result_df = joined_gdf[ - ["vill_ID", "vill_name", "Asset ID", "creation_t", "WorkCatego"] - ].copy() - - # 7. Add villages that have no assets - villages_with_assets = result_df["vill_ID"].unique() - no_asset_villages = village_gdf[ - ~village_gdf["vill_ID"].isin(villages_with_assets) - ].copy() - no_asset_villages["Asset ID"] = "No Asset" - no_asset_villages["creation_t"] = "No Asset" - no_asset_villages["WorkCatego"] = "No Asset" - - result_df = pd.concat( - [ - result_df, - no_asset_villages[ - ["vill_ID", "vill_name", "Asset ID", "creation_t", "WorkCatego"] - ], - ], - ignore_index=True, - ) - - print("Processing complete") - - # 8. Create Excel file - create_excel_village_nrega_assets( - result_df, - output_file, - writer, - village_gdf[["vill_ID", "vill_name"]], - start_year, - end_year, - ) - - return result_df, village_gdf[["vill_ID", "vill_name"]] - - -def analyze_results(village_asset_count, village_gdf): - villages_with_counts = village_gdf.merge( - village_asset_count, on="vill_ID", how="left" - ) - villages_with_counts["asset_count"] = villages_with_counts["asset_count"].fillna(0) - return villages_with_counts - - -def create_excel_crop_inten(data, output_file, writer, start_year, end_year): - df_data = [] - - features = data["features"] - for feature in features: - properties = feature.get("properties", {}) - uid = properties.get("uid", "Unknown") - row = {"UID": uid, "area_in_ha": properties.get("area_in_ha", 0)} - - # Process each year in range using new key naming convention - for year in range(start_year, end_year + 1): - cropping_key = f"cropping_intensity_{year}" - single_c_key = f"single_cropped_area_{year}" - single_k_key = f"single_kharif_cropped_area_{year}" - single_n_key = f"single_non_kharif_cropped_area_{year}" - doubly_c_key = f"doubly_cropped_area_{year}" - triply_c_key = f"triply_cropped_area_{year}" - - row[f"cropping_intensity_unit_less_{year}-{year + 1}"] = properties.get( - cropping_key, 0 - ) - row[f"single_cropped_area_in_ha_{year}-{year + 1}"] = properties.get( - single_c_key, 0 - ) - row[f"single_kharif_cropped_area_in_ha_{year}-{year + 1}"] = properties.get( - single_k_key, 0 - ) - row[f"single_non_kharif_cropped_area_in_ha_{year}-{year + 1}"] = ( - properties.get(single_n_key, 0) - ) - row[f"doubly_cropped_area_in_ha_{year}-{year + 1}"] = properties.get( - doubly_c_key, 0 - ) - row[f"triply_cropped_area_in_ha_{year}-{year + 1}"] = properties.get( - triply_c_key, 0 - ) - - row["sum_area_in_ha"] = properties.get("sum", 0) / 10000 - df_data.append(row) - - # Create and format DataFrame - df = pd.DataFrame(df_data) - df = df.sort_values(["UID"]) - - # Round numeric columns - numeric_cols = df.select_dtypes(include=["int64", "float64"]).columns - df[numeric_cols] = df[numeric_cols].round(2) - - # Write to Excel - df.to_excel(writer, sheet_name="croppingIntensity_annual", index=False) - print("Excel file created for cropping intensity.") - - -def create_excel_crop_drou(data, output_file, writer, start_year, end_year): - df_data = [] - - features = data["features"] - for feature in features: - properties = feature.get("properties", {}) - row = {"UID": properties.get("uid", "Unknown ID")} - - for year in range(start_year, end_year + 1): - drlb_key = f"drlb_{year}" - drysp_key = f"drysp_{year}" - kh_cr_key = f"kh_cr_{year}" - m_ons_key = f"m_ons_{year}" - pcr_k_key = f"pcr_k_{year}" - t_wks_key = f"t_wks_{year}" - - # Get drought levels (drlb) and count occurrences - drlb_value = properties.get(drlb_key, "") - row[f"No_Drought_in_weeks_{year}"] = drlb_value.count("0") - row[f"Mild_in_weeks_{year}"] = drlb_value.count("1") - row[f"Moderate_in_weeks_{year}"] = drlb_value.count("2") - row[f"Severe_in_weeks_{year}"] = drlb_value.count("3") - - # Add other properties - row[f"drysp_unit_4_weeks_{year}"] = properties.get(drysp_key, "0") - row[f"kharif_cropped_sqkm_{year}"] = properties.get(kh_cr_key, "0") - row[f"monsoon_onset_{year}"] = properties.get(m_ons_key, "0") - row[f"kharif_cropped_area_percent_{year}"] = properties.get(pcr_k_key, "0") - row[f"total_weeks_{year}"] = properties.get(t_wks_key, "0") - - df_data.append(row) - - # Create DataFrame - df = pd.DataFrame(df_data) - df = df.sort_values(["UID"]) - - ## for roundoff all numeric value upto 2 decimal - numeric_cols = df.select_dtypes(include=["int64", "float64"]).columns - df[numeric_cols] = df[numeric_cols].round(2) - - # Write to Excel - df.to_excel(writer, sheet_name="croppingDrought_kharif", index=False) - print("Excel file created for cropping drought.") - - -def parse_geojson_annual_mws(data): - features = data["features"] - - all_data = defaultdict(lambda: defaultdict(dict)) - - for feature in features: - properties = feature["properties"] - uid = properties.get("uid", "Unknown") - - for key, value in properties.items(): - if isinstance(key, str) and isinstance(value, str): - if key.startswith("20") and len(key) == 9: - year = key - try: - # Attempt to parse the value as JSON - year_data = json.loads(value.replace("'", '"')) - all_data[uid][year] = year_data - except Exception as e: - print(f"Couldn't parse data for {uid}, {key}: {e}") - - return all_data - - -def create_excel_annual_mws(data, output_file, writer): - df_data = [] - year_columns = ["ET", "RunOff", "G", "DeltaG", "Precipitation", "WellDepth"] - - for uid, years in data.items(): - row = {"UID": uid} - - for year, metrics in years.items(): - start_year = year[:4] - end_year = str(int(start_year) + 1) - formatted_year = f"{start_year}-{end_year}" - - for col in year_columns: - if col == "WellDepth": - column_name = f"{col}_in_m_{formatted_year}" - row[column_name] = metrics.get(col, "N/A") - else: - column_name = f"{col}_in_mm_{formatted_year}" - row[column_name] = metrics.get(col, "N/A") - - df_data.append(row) - - df = pd.DataFrame(df_data) - df = df.sort_values(["UID"]) - - ## for roundoff all numeric value upto 2 decimal - numeric_cols = df.select_dtypes(include=["int64", "float64"]).columns - df[numeric_cols] = df[numeric_cols].round(2) - - df.to_excel(writer, sheet_name="hydrological_annual", index=False) - print("Excel file created for hydrological_annual") - - -def parse_json_seas_mws(file_path): - with open(file_path, "r") as file: - data = json.load(file) - return data - - -def get_season(month): - if month in (3, 4, 5, 6): - return "zaid" - elif month in (7, 8, 9, 10): - return "kharif" - elif month in (11, 12, 1, 2): - return "rabi" - - -def process_feature(feature): - uid = feature["properties"]["uid"] - results = { - "UID": uid, - "precipitation": {"kharif": {}, "rabi": {}, "zaid": {}}, - "et": {"kharif": {}, "rabi": {}, "zaid": {}}, - "runoff": {"kharif": {}, "rabi": {}, "zaid": {}}, - "delta g": {"kharif": {}, "rabi": {}, "zaid": {}}, - "g": {"kharif": {}, "rabi": {}, "zaid": {}}, - } - - variable_mapping = { - "Precipitation": "precipitation", - "ET": "et", - "RunOff": "runoff", - "DeltaG": "delta g", - "G": "g", - } - - for key, value in feature["properties"].items(): - if key.startswith("20"): - try: - date = datetime.strptime(key, "%Y-%m-%d") - year = date.year - month = date.month - season = get_season(month) - if season == "rabi": - current_year = year - 1 if month in (1, 2) else year - elif season == "zaid": - current_year = year - 1 if month in (3, 4, 5, 6) else year - else: - current_year = year - data = json.loads(value) - - for json_var, result_var in variable_mapping.items(): - if json_var in data: - if current_year not in results[result_var][season]: - results[result_var][season][current_year] = 0.0 - results[result_var][season][current_year] += float( - data[json_var] - ) - - except (ValueError, json.JSONDecodeError) as e: - print(f"Error processing data for date {key}: {e}") - continue - return results - - -def create_excel_seas_mws(processed_data, output_file, writer, start_year, end_year): - variables = ["precipitation", "et", "runoff", "delta g", "g"] - seasons = ["kharif", "rabi", "zaid"] - - data = {"UID": []} - for variable in variables: - for year in range(start_year, end_year + 1): - for season in seasons: - end_to_year = year + 1 - column_name = f"{variable}_{season}_in_mm_{year}-{end_to_year}" - data[column_name] = [] - - for feature_data in processed_data: - data["UID"].append(feature_data["UID"]) - for variable in variables: - for year in range(start_year, end_year + 1): - for season in seasons: - end_to_year = year + 1 - column_name = f"{variable}_{season}_in_mm_{year}-{end_to_year}" - value = feature_data[variable].get(season, {}).get(year, 0.0) - data[column_name].append(value) - - df = pd.DataFrame(data) - df = df.sort_values("UID") - - ## for roundoff all numeric value upto 2 decimal - numeric_cols = df.select_dtypes(include=["int64", "float64"]).columns - df[numeric_cols] = df[numeric_cols].round(2) - - df.to_excel(writer, sheet_name="hydrological_seasonal", index=False) - print(f"Excel file created hydrological_seasonal") - - -def create_excel_for_village_boun(old_geojson, writer): - results = [] - - village_data = {} - - for feature in old_geojson["features"]: - properties = feature["properties"] - - # Extract properties - state_census_ID = properties.get("state_cen", None) - dist_census_ID = properties.get("dist_cen", None) - block_census_ID = properties.get("block_cen", None) - village_id = properties.get("vill_ID", None) - village_name = properties.get("vill_name", None) - - # Initialize village data using village_id as the key - if village_id not in village_data: - village_data[village_id] = { - "village_name": village_name, - "TOT_P": 0, - "P_LIT": 0, - "P_SC": 0, - "P_ST": 0, - "state_census_ID": state_census_ID, - "dist_census_ID": dist_census_ID, - "block_census_ID": block_census_ID, - "geometry": feature["geometry"], - } - - village_data[village_id]["TOT_P"] += properties.get("TOT_P", 0) - village_data[village_id]["P_LIT"] += properties.get("P_LIT", 0) - village_data[village_id]["P_SC"] += properties.get("P_SC", 0) - village_data[village_id]["P_ST"] += properties.get("P_ST", 0) - - for village_id, data in village_data.items(): - total_popu = data["TOT_P"] - literacy_rate = data["P_LIT"] * 100 / total_popu if total_popu > 0 else 0.0 - total_SC_popu = data["P_SC"] - total_ST_popu = data["P_ST"] - sc_perce = (data["P_SC"] * 100 / total_popu) if total_popu > 0 else 0.0 - st_perce = (data["P_ST"] * 100 / total_popu) if total_popu > 0 else 0.0 - - results.append( - { - "state_census_ID": data["state_census_ID"], - "dist_census_ID": data["dist_census_ID"], - "block_census_ID": data["block_census_ID"], - "village_id": village_id, - "village_name": data["village_name"], - "total_population_count": total_popu, - "total_SC_population_count": total_SC_popu, - "total_ST_population_count": total_ST_popu, - "literacy_rate_percent": literacy_rate, - "SC_percent": sc_perce, - "ST_percent": st_perce, - } - ) - - results_df = pd.DataFrame(results) - results_df.to_excel(writer, sheet_name="social_economic_indicator", index=False) - - print(f"Excel file created for social_economic_indicator") - - -def download_layers_excel_file(state, district, block): - base_path = os.path.join(EXCEL_PATH, "data/stats_excel_files") - state_path = os.path.join(base_path, state.upper()) - district_path = os.path.join(state_path, district.upper()) - filename = f"{district}_{block}.xlsx" - file_path = os.path.join(district_path, filename) - - output_dir = Path(file_path).parent - output_dir.mkdir(parents=True, exist_ok=True) - - if not os.path.exists(file_path): - try: - if not get_vector_layer_geoserver(state, district, block): - return Response( - { - "status": "error", - "message": "Failed to generate vector layer from GeoServer.", - }, - status=status.HTTP_500_INTERNAL_SERVER_ERROR, - ) - if not os.path.exists(file_path): - return Response( - {"status": "error", "message": "Failed to generate Excel file."}, - status=status.HTTP_500_INTERNAL_SERVER_ERROR, - ) - - except Exception as e: - return Response( - { - "status": "error", - "message": f"Error during file generation: {str(e)}", - }, - status=status.HTTP_500_INTERNAL_SERVER_ERROR, - ) - else: - print(f"Excel file already exists at: {file_path}") - - # Single file reading logic - only written once! - if os.path.exists(file_path): - try: - with open(file_path, "rb") as file: - response = HttpResponse( - file.read(), - content_type="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", - ) - response["Content-Disposition"] = f"attachment; filename={filename}" - return response - except Exception as e: - return Response( - {"status": "error", "message": f"Error reading file: {str(e)}"}, - status=status.HTTP_500_INTERNAL_SERVER_ERROR, - ) - else: - return Response( - {"status": "error", "message": "Failed to locate Excel file."}, - status=status.HTTP_404_NOT_FOUND, - ) - - -def generate_stats_excel_file(state, district, block): - """ - Deletes existing Excel layer file and forces regeneration. - Then returns the newly generated file. - """ - base_path = os.path.join(EXCEL_PATH, "data/stats_excel_files") - state_path = os.path.join(base_path, state.upper()) - district_path = os.path.join(state_path, district.upper()) - filename = f"{district}_{block}.xlsx" - file_path = os.path.join(district_path, filename) - - try: - # Create directory if it doesn't exist - output_dir = Path(file_path).parent - output_dir.mkdir(parents=True, exist_ok=True) - - # ALWAYS delete existing file if it exists - if os.path.exists(file_path): - os.remove(file_path) - - from .mws_indicators import generate_mws_data_for_kyl_filters - from .village_indicators import get_generate_filter_data_village - from public_api.views import get_tehsil_json - - get_vector_layer_geoserver(state, district, block) - get_tehsil_json(state, district, block, 1) - generate_mws_data_for_kyl_filters(state, district, block, "json", 1) - get_generate_filter_data_village(state, district, block, 1) - - if not os.path.exists(file_path): - return Response( - { - "status": "error", - "message": "Excel file generation completed but file not found.", - }, - status=status.HTTP_500_INTERNAL_SERVER_ERROR, - ) - - # Return the newly generated file - with open(file_path, "rb") as file: - response = HttpResponse( - file.read(), - content_type="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", - ) - response["Content-Disposition"] = f"attachment; filename={filename}" - return response - - except Exception as e: - return Response( - {"status": "error", "message": f"Failed to generate stats excel: {str(e)}"}, - status=status.HTTP_500_INTERNAL_SERVER_ERROR, - ) - - -def add_sheets_to_excel(state, district, block, sheets): - try: - sheets_to_add = [sheet.strip() for sheet in sheets.split(",") if sheet.strip()] - results = [] - - for sheet in sheets_to_add: - r = get_vector_layer_geoserver(state, district, block, sheet) - results.append(r) - - from .mws_indicators import generate_mws_data_for_kyl_filters - from .village_indicators import get_generate_filter_data_village - - from public_api.views import get_tehsil_json - - get_tehsil_json(state, district, block, 1) - generate_mws_data_for_kyl_filters(state, district, block, "json", 1) - get_generate_filter_data_village(state, district, block, 1) - - successful = [r for r in results if r["status"] == "success"] - failed = [r for r in results if r["status"] == "failed"] - - response_data = { - "status": "success" if successful else "failed", - "message": f"Stats data added info. {len(successful)} successful, {len(failed)} failed.", - } - - return response_data - - except Exception as e: - return { - "status": "error", - "message": str(e), - } +import os +import requests, json +from django.http import HttpResponse, Http404 +from rest_framework.response import Response +import pandas as pd +import geopandas as gpd +from collections import defaultdict +from datetime import datetime +from nrm_app.settings import GEOSERVER_URL, EXCEL_PATH +import numpy as np +from shapely.geometry import Point, shape +from .models import LayerInfo +from django.http import HttpResponse +from rest_framework import status +from pathlib import Path + + +def fetch_layers_for_excel_generation(): + """ + Fetch all vector layers where `excel_to_be_generated` is True. + """ + layers = LayerInfo.objects.filter( + layer_type="vector", excel_to_be_generated=True + ).values("layer_name", "workspace", "start_year", "end_year") + return list(layers) + + +def get_url(workspace, layer_name): + """Construct the GeoServer WFS request URL for fetching GeoJSON data.""" + geojson_url = f"{GEOSERVER_URL}/{workspace}/ows?service=WFS&version=1.0.0&request=GetFeature&typeName={workspace}:{layer_name}&outputFormat=application/json" + return geojson_url + + +def get_vector_layer_geoserver(state, district, block, specific_sheets=None): + print(f"Generate Stats excel for {state}_{district}_{block}") + base_path = os.path.join(EXCEL_PATH, "data/stats_excel_files") + district_path = os.path.join( + base_path, state.replace(" ", "_").upper(), district.replace(" ", "_").upper() + ) + os.makedirs(district_path, exist_ok=True) + xlsx_file = os.path.join(district_path, f"{district}_{block}.xlsx") + + workspaces_to_process = specific_sheets + + # Handle existing sheets when adding specific workspaces + results = [] + file_exists = os.path.exists(xlsx_file) + mode = "a" if file_exists else "w" + + # Use append mode with if_sheet_exists='replace' + with pd.ExcelWriter( + xlsx_file, + engine="openpyxl", + mode=mode, + if_sheet_exists="replace" if mode == "a" else None, + ) as writer: + for layer in fetch_layers_for_excel_generation(): + workspace = layer["workspace"] + + if workspaces_to_process and workspace not in workspaces_to_process: + continue + + start_year = layer.get("start_year") + end_year = layer.get("end_year") + + if "{district}" in layer["layer_name"] and "{block}" in layer["layer_name"]: + layer_name = layer["layer_name"].format(district=district, block=block) + else: + layer_name = layer["layer_name"] + + print(f"Processing layer: {layer_name}") + print(f"Workspace for the layer is: {workspace}") + + geojson_data = None + try: + url = get_url(workspace, layer_name) + response = requests.get(url) + response.raise_for_status() + geojson_data = response.json() + except requests.exceptions.RequestException as e: + print(f"Failed to fetch data for {layer_name}: {e}") + results.append( + {"layer": layer_name, "status": "failed", "workspace": workspace} + ) + continue + + # Process the data based on workspace + if workspace == "terrain": + create_excel_for_terrain(geojson_data, xlsx_file, writer) + elif ( + workspace == "terrain_lulc" + and layer_name == f"{district}_{block}_lulc_slope" + ): + create_excel_for_terrain_lulc_slope(geojson_data, xlsx_file, writer) + elif ( + workspace == "terrain_lulc" + and layer_name == f"{district}_{block}_lulc_plain" + ): + create_excel_for_terrain_lulc_plain(geojson_data, xlsx_file, writer) + elif workspace == "swb": + create_excel_for_swb( + geojson_data, xlsx_file, writer, start_year, end_year + ) + create_excel_for_mws_intersect_swb( + geojson_data, writer, district, block + ) + elif workspace == "nrega_assets": + mws_lay_name = f"deltaG_well_depth_{district}_{block}" + mws_file_url = get_url("mws_layers", mws_lay_name) + + try: + response = requests.get(mws_file_url) + response.raise_for_status() + mws_geojson_datas = response.json() + except requests.exceptions.RequestException as e: + print(f"Failed to fetch MWS data: {e}") + continue + + create_excel_for_nrega_assets( + geojson_data, + mws_geojson_datas, + xlsx_file, + writer, + start_year, + end_year, + ) + try: + fetch_village_asset_count( + state, district, block, writer, xlsx_file, start_year, end_year + ) + except Exception as e: + print("Exception as e", str(e)) + + elif workspace == "crop_intensity": + create_excel_crop_inten( + geojson_data, xlsx_file, writer, start_year, end_year + ) + elif workspace == "drought": + create_excel_crop_drou( + geojson_data, xlsx_file, writer, start_year, end_year + ) + elif ( + workspace == "mws_layers" + and layer_name == f"deltaG_well_depth_{district}_{block}" + ): + parsed_data_annual_mws = parse_geojson_annual_mws(geojson_data) + create_excel_annual_mws(parsed_data_annual_mws, xlsx_file, writer) + try: + create_excel_mws_inters_villages( + geojson_data, xlsx_file, writer, district, block + ) + except Exception as e: + print("Exception", str(e)) + elif ( + workspace == "mws_layers" + and layer_name == f"deltaG_fortnight_{district}_{block}" + ): + processed_data = [ + process_feature(feature) for feature in geojson_data["features"] + ] + create_excel_seas_mws( + processed_data, xlsx_file, writer, start_year, end_year + ) + elif workspace == "panchayat_boundaries": + create_excel_for_village_boun(geojson_data, writer) + elif workspace == "drought_causality": + create_excel_for_drought_causality( + geojson_data, xlsx_file, writer, start_year, end_year + ) + elif workspace == "ccd": + create_excel_for_ccd( + geojson_data, xlsx_file, writer, start_year, end_year + ) + elif workspace == "canopy_height": + create_excel_for_ch( + geojson_data, xlsx_file, writer, start_year, end_year + ) + elif workspace == "tree_overall_ch": + create_excel_for_overall_tree_change(geojson_data, xlsx_file, writer) + elif ( + workspace == "change_detection" + and layer_name == f"change_vector_{district}_{block}_Afforestation" + ): + create_excel_chan_detection_afforestation( + geojson_data, xlsx_file, writer + ) + elif ( + workspace == "change_detection" + and layer_name == f"change_vector_{district}_{block}_CropIntensity" + ): + create_excel_chan_detection_cropintensity( + geojson_data, xlsx_file, writer + ) + elif ( + workspace == "change_detection" + and layer_name == f"change_vector_{district}_{block}_Deforestation" + ): + create_excel_chan_detection_deforestation( + geojson_data, xlsx_file, writer + ) + elif ( + workspace == "change_detection" + and layer_name == f"change_vector_{district}_{block}_Degradation" + ): + create_excel_chan_detection_degradation(geojson_data, xlsx_file, writer) + elif ( + workspace == "change_detection" + and layer_name == f"change_vector_{district}_{block}_Urbanization" + ): + create_excel_chan_detection_urbanization( + geojson_data, xlsx_file, writer + ) + elif workspace == "restoration": + create_excel_for_restoration(geojson_data, xlsx_file, writer) + elif workspace == "aquifer": + create_excel_for_aquifer(geojson_data, writer) + elif workspace == "soge": + create_excel_for_soge(geojson_data, xlsx_file, writer) + elif workspace == "lcw": + create_excel_for_lcw(geojson_data, writer) + elif workspace == "agroecological": + create_excel_for_agroecological(geojson_data, writer) + elif workspace == "factory_csr": + create_excel_for_factory_csr(geojson_data, writer) + elif workspace == "green_credit": + create_excel_for_green_credit(geojson_data, writer) + elif workspace == "mining": + create_excel_for_mining(geojson_data, writer) + elif workspace == "stream_order": + create_excel_for_stream_order(geojson_data, writer) + elif workspace == "mws_connectivity": + create_excel_for_mws_connectivity(geojson_data, writer) + elif workspace == "mws": + create_excel_for_mws(geojson_data, writer) + elif workspace == "facilities_proximity": + create_excel_for_facilities(geojson_data, writer) + elif workspace == "dem": + create_excel_for_dem(geojson_data, writer) + elif workspace == "canal": + create_excel_for_canal(geojson_data, writer) + elif workspace == "river": + create_excel_for_river(geojson_data, writer) + elif workspace == "lulc_vector": + create_excel_for_lulc_vector(geojson_data, writer, start_year, end_year) + elif workspace == "drainage_density": + create_excel_for_drainage_density(geojson_data, writer) + elif workspace == "antyodaya_2020": + create_excel_for_antyodaya_20(geojson_data, writer) + elif workspace == "livestocks": + create_excel_for_livestock(geojson_data, writer) + + results.append( + {"layer": layer_name, "status": "success", "workspace": workspace} + ) + + return results + + +def create_excel_for_livestock(data, writer): + try: + features = data.get("features", []) + df_data = [feature.get("properties", {}) for feature in features] + df = pd.DataFrame(df_data) + + # Columns to exclude + exclude_cols = ["state_name", "district_name", "TEHSIL"] + df = df.drop(columns=exclude_cols, errors="ignore") + + # Keep important columns first if they exist + first_cols = [c for c in ["pc11_village_id", "NAME"] if c in df.columns] + other_cols = [c for c in df.columns if c not in first_cols] + df = df[first_cols + other_cols] + + df.to_excel(writer, sheet_name="livestock", index=False) + print("Excel file created for livestock") + except Exception as e: + print(f"Error in getting livestock data: {e}") + + +def create_excel_for_antyodaya_20(data, writer): + try: + features = data.get("features", []) + df_data = [feature.get("properties", {}) for feature in features] + df = pd.DataFrame(df_data) + + # Keep important columns first if they exist + first_cols = [c for c in ["village_id", "village_name"] if c in df.columns] + other_cols = [c for c in df.columns if c not in first_cols] + df = df[first_cols + other_cols] + + # Round numeric columns + numeric_cols = df.select_dtypes(include=["number"]).columns + df[numeric_cols] = df[numeric_cols].round(2) + df.to_excel(writer, sheet_name="antyodaya", index=False) + print("Excel file created for antyodaya") + except Exception as e: + print(f"Error in getting antyodaya data: {e}") + + +def create_excel_for_drainage_density(data, writer): + import ast + + print("Inside create_excel_for Drainage Density") + df_data = [] + features = data["features"] + + for feature in features: + properties = feature["properties"] + row = { + "UID": properties.get("uid", ""), + "area_in_ha": properties.get("area_in_ha", ""), + "drainage_density_in_km_per_km2": properties.get( + "drainage_density_weighted", "" + ), + "drainage_density_std_in_km_per_km2": properties.get( + "drainage_density_std", "" + ), + "stream_order_length_in_km": sum( + ast.literal_eval(properties.get("stream_length_km", "")) + ), + } + + df_data.append(row) + + df = pd.DataFrame(df_data) + df = df.sort_values(["UID"]) + numeric_cols = df.select_dtypes(include=["int64", "float64"]).columns + df[numeric_cols] = df[numeric_cols].round(2) + df.to_excel(writer, sheet_name="drainage_density", index=False) + print("Excel file created for Drainage Density") + + +def create_excel_for_lulc_vector(data, writer, start_year, end_year): + df_data = [] + features = data["features"] + years = list(range(start_year, end_year + 1)) + + classes = { + "barrenland": ("barrenland", "barrenla"), + "built_up_area": ("built-up_a", "built-up"), + "cropland": ("cropland_a", "cropland"), + "double_crop": ("doubly_cro", "doubly_c"), + "triple_crop": ("triply_cro", "triply_c"), + "tree_forest": ("tree_fores", "tree_for"), + "shrub_scrub": ("shrub_scru", "shrub_sc"), + "single_kharif": ("single_kha", "single_k"), + "single_non_kharif": ("single_non", "single_n"), + "k_water": ("k_water_ar", "k_water_"), + "kr_water": ("kr_water_a", "kr_water"), + "krz_water": ("krz_water_", "krz_wate"), + } + + def get_key(base_key, trunc_prefix, idx): + """Derive the property key for a given year index.""" + if idx == 0: + return base_key + return f"{trunc_prefix}_{idx}" + + for feature in features: + properties = feature["properties"] + + row = { + "UID": properties.get("uid", ""), + "area_in_ha": properties.get("area_in_ha", ""), + "sum_in_ha": (properties.get("sum") or 0) / 10000, + } + + for idx, year in enumerate(years): + for class_name, (base_key, trunc_prefix) in classes.items(): + key = get_key(base_key, trunc_prefix, idx) + row[f"{class_name}_in_ha_{year}"] = properties.get(key, 0) + + df_data.append(row) + + df = pd.DataFrame(df_data) + df = df.sort_values(["UID"]) + numeric_cols = df.select_dtypes(include=["int64", "float64"]).columns + df[numeric_cols] = df[numeric_cols].round(2) + df.to_excel(writer, sheet_name="lulc_vector", index=False) + print("Excel file created for lulc vector") + + +def create_excel_for_canal(data, writer): + print("Inside create_excel_for Canal") + try: + df_data = [] + features = data["features"] + + for feature in features: + properties = feature["properties"] + row = { + "UID": properties.get("uid", ""), + "project_name": properties.get("prjname", ""), + "canal_code": properties.get("cancode", ""), + "canal_name": properties.get("canname", ""), + } + + df_data.append(row) + + df = pd.DataFrame(df_data) + df = df.sort_values(["UID"]) + df.to_excel(writer, sheet_name="canal", index=False) + print("Excel file created for canal") + except Exception as e: + print("Canal Layer not found :: ", e) + + +def create_excel_for_river(data, writer): + print("Inside create_excel_for River") + try: + df_data = [] + features = data["features"] + + for feature in features: + properties = feature["properties"] + row = { + "UID": properties.get("uid", ""), + "river_name": properties.get("rivname", ""), + } + + df_data.append(row) + + df = pd.DataFrame(df_data) + df = df.sort_values(["UID"]) + df.to_excel(writer, sheet_name="river", index=False) + print("Excel file created for river") + except Exception as e: + print("River Layer not found :: ", e) + + +def create_excel_for_dem(data, writer): + print("Inside create_excel_for DEM") + + df_data = [] + features = data["features"] + + for feature in features: + properties = feature["properties"] + + row = { + "UID": properties.get("uid", ""), + "min_elevation_in_m": properties.get("min_elevation", ""), + "max_elevation_in_m": properties.get("max_elevation", ""), + "mean_elevation_in_m": properties.get("mean_elevation", ""), + } + + df_data.append(row) + + df = pd.DataFrame(df_data) + df = df.sort_values(["UID"]) + numeric_cols = df.select_dtypes(include=["int64", "float64"]).columns + df[numeric_cols] = df[numeric_cols].round(2) + df.to_excel(writer, sheet_name="dem", index=False) + print("Excel file created for dem") + + +def create_excel_for_mws_intersect_swb(swb_geojson, writer, district, block): + print("Inside create_excel_for_mws_intersect_swb") + + # --- Fetch MWS layer --- + mws_layer_name = f"mws_{district}_{block}" + mws_data_url = get_url("mws", mws_layer_name) + + mws_response = requests.get(mws_data_url) + if mws_response.status_code != 200: + print(f"Error fetching MWS data: {mws_response.status_code}") + return + + mws_geojson = mws_response.json() + + def calculate_intersection_area(geom1, geom2): + if geom1.intersects(geom2): + return geom1.intersection(geom2).area + return 0 + + rows = [] + + for mws_feature in mws_geojson["features"]: + mws_props = mws_feature["properties"] + mws_uid = mws_props.get("uid") + mws_geom = shape(mws_feature["geometry"]) + + for swb_feature in swb_geojson["features"]: + swb_props = swb_feature["properties"] + swb_geom = shape(swb_feature["geometry"]) + + intersection_area = calculate_intersection_area(mws_geom, swb_geom) + + if intersection_area > 0: + # waterbodies centroid calculation + centroid = swb_geom.centroid + lon, lat = centroid.x, centroid.y + + rows.append( + { + "UID": mws_uid, + "SWB_UID": swb_props.get("UID"), + "Waterbodies_name": swb_props.get("water_body_name"), + "Latitude": lat, + "Longitude": lon, + } + ) + + df = pd.DataFrame(rows) + + if not df.empty: + numeric_cols = df.select_dtypes(include=["int64", "float64"]).columns + df[numeric_cols] = df[numeric_cols].round(2) + + df.to_excel(writer, sheet_name="mws_intersect_swb", index=False) + print("Excel sheet 'mws_intersect_swb' created successfully") + + +def create_excel_for_facilities(data, writer): + try: + features = data["features"] + df_data = [feature["properties"] for feature in features] + + df = pd.DataFrame(df_data) + + first_cols = ["censuscode2011", "censusname"] + other_cols = [c for c in df.columns if c not in first_cols] + df = df[first_cols + other_cols] + + numeric_cols = df.select_dtypes(include=["int64", "float64"]).columns + df[numeric_cols] = df[numeric_cols].round(2) + + exclude_cols = [ + "censuscode2011", + "censusname", + "district", + "core_admin_uid", + "shrid2", + "state", + "tehsil", + ] + df.rename( + columns={ + col: f"{col}_in_km" for col in df.columns if col not in exclude_cols + }, + inplace=True, + ) + + # Write to Excel + df.to_excel(writer, sheet_name="facilities_proximity", index=False) + + print("Excel file created for facilities_proximity") + except Exception as e: + print("facilities_proximity Layer not found :: ", e) + + +def create_excel_for_mws(data, writer): + df_data = [] + features = data["features"] + + for feature in features: + properties = feature["properties"] + row = { + "UID": properties.get("uid", ""), + "area_in_ha": properties.get("area_in_ha", ""), + "watershed_code": properties.get("wsconc", ""), + "basin_code": properties.get("bacode", ""), + "sub_basin_code": properties.get("sbcode", ""), + } + + df_data.append(row) + + df = pd.DataFrame(df_data) + df = df.sort_values(["UID"]) + df.to_excel(writer, sheet_name="mws", index=False) + print("Excel file created for mws") + + +def create_excel_for_mws_connectivity(data, writer): + """ + direction_map = { + '0': 'No Direction', + '1': 'North-East', + '2': 'East', + '3': 'South-East', + '4': 'South', + '5': 'South-West', + '6': 'West', + '7': 'North-West', + '8': 'North' + } + """ + df_data = [] + features = data["features"] + + for feature in features: + properties = feature["properties"] + row = { + "UID": properties["uid"], + "direction": properties["direction"], + "downstream_mws": properties["downstream"], + "upstream_mws": properties["upstream"], + } + + df_data.append(row) + df = pd.DataFrame(df_data) + df.replace("", "unknown", inplace=True) + df = df.sort_values(["UID"]) + df.to_excel(writer, sheet_name="mws_connectivity", index=False) + print("Excel file created for mws_connectivity") + + +def create_excel_for_stream_order(data, writer): + df_data = [] + features = data["features"] + + for feature in features: + properties = feature["properties"] + row = { + "UID": properties["uid"], + "order_1_area_percent": properties["1"], + "order_2_area_percent": properties["2"], + "order_3_area_percent": properties["3"], + "order_4_area_percent": properties["4"], + "order_5_area_percent": properties["5"], + "order_6_area_percent": properties["6"], + "order_7_area_percent": properties["7"], + "order_8_area_percent": properties["8"], + "order_9_area_percent": properties["9"], + "order_10_area_percent": properties["10"], + "order_11_area_percent": properties["11"], + } + + df_data.append(row) + df = pd.DataFrame(df_data) + df.replace("", "unknown", inplace=True) + df = df.sort_values(["UID"]) + df.to_excel(writer, sheet_name="stream_order", index=False) + print("Excel file created for stream order") + + +def create_excel_for_mining(data, writer): + try: + df_data = [] + features = data["features"] + + for feature in features: + properties = feature["properties"] + row = { + "UID": properties["uid"], + "division": properties["company_na"], + "proposal": properties["proposal"], + "sector_moefcc": properties["sector_moe"], + "village": properties["village"], + } + + df_data.append(row) + df = pd.DataFrame(df_data) + df.replace("", "unknown", inplace=True) + df = df.sort_values(["UID"]) + df.to_excel(writer, sheet_name="mining", index=False) + print("Excel file created for mining") + except Exception as e: + print("Mining Layer not found :: ", e) + + +def create_excel_for_green_credit(data, writer): + try: + df_data = [] + features = data["features"] + + for feature in features: + properties = feature["properties"] + row = { + "UID": properties["uid"], + "division": properties["division"], + # "parcel_id": properties["parcel_id"], + "land_info": properties["land_info"], + "kml_url": properties["kml_url"], + } + + df_data.append(row) + df = pd.DataFrame(df_data) + df = df.sort_values(["UID"]) + df.to_excel(writer, sheet_name="green_credit", index=False) + print("Excel file created for green_credit") + except Exception as e: + print("green credit Layer not found :: ", e) + + +def create_excel_for_factory_csr(data, writer): + try: + df_data = [] + features = data["features"] + + for feature in features: + properties = feature["properties"] + row = { + "UID": properties["uid"], + "Company_Name": properties["COMPANY NA"], + "ADDRESS": properties["ADDRESS"], + "LOCATION T": properties["LOCATION T"], + } + + df_data.append(row) + df = pd.DataFrame(df_data) + df = df.sort_values(["UID"]) + df.to_excel(writer, sheet_name="factory_csr", index=False) + print("Excel file created for factory_csr") + except Exception as e: + print("factory csr Layer not found :: ", e) + + +def create_excel_for_agroecological(data, writer): + try: + df_data = [] + features = data["features"] + + for feature in features: + properties = feature["properties"] + row = { + "UID": properties["uid"], + "organization_name": properties["organization_name"], + "organization_type": properties["organization_type"], + "created_at": properties["created_at"], + "contact_person": properties["contact_person"], + "email": properties["email"], + "domains": properties["domains"], + } + + df_data.append(row) + df = pd.DataFrame(df_data) + df = df.sort_values(["UID"]) + df.to_excel(writer, sheet_name="agroecological", index=False) + print("Excel file created for agroecological") + except Exception as e: + print("agroecological Layer not found :: ", e) + + +def create_excel_for_lcw(data, writer): + try: + df_data = [] + features = data["features"] + + for feature in features: + properties = feature["properties"] + row = { + "UID": properties["uid"], + "title_of_conflict": properties["Title of Conflict"], + "link_to_conflict": properties["Link to conflict"], + } + + df_data.append(row) + df = pd.DataFrame(df_data) + df = df.sort_values(["UID"]) + df.to_excel(writer, sheet_name="lcw_conflict", index=False) + print("Excel file created for lcw_conflict") + except Exception as e: + print("lcw Layer not found :: ", e) + + +def create_excel_for_soge(data, xlsx_file, writer): + df_data = [] + features = data["features"] + + for feature in features: + properties = feature["properties"] + row = { + "UID": properties["uid"], + "area_in_ha": properties["area_in_ha"], + "soge_dev_percent": properties["sgw_dev_pe"], + "class_code": properties["code"], + "class_name": properties["class"], + } + + df_data.append(row) + df = pd.DataFrame(df_data) + df = df.sort_values(["UID"]) + + ## for roundoff all numeric value upto 2 decimal + numeric_cols = df.select_dtypes(include=["int64", "float64"]).columns + df[numeric_cols] = df[numeric_cols].round(2) + + df.to_excel(writer, sheet_name="soge_vector", index=False) + print(f"Excel file created for soge_vector") + + +def create_excel_for_aquifer(data, writer): + df_data = [] + features = data["features"] + + for feature in features: + properties = feature["properties"] + if properties.get("aquifer_class", "") == "Hard-Rock": + aquifer_class = "Hard Rock" + else: + aquifer_class = "Alluvium" + row = { + "UID": properties.get("uid", ""), + "area_in_ha": properties.get("area_in_ha", ""), + "aquifer_class": aquifer_class, + "principle_aq_Alluvium_percent": properties.get( + "principle_aq_Alluvium_percent", "0" + ), + "principle_aq_Banded Gneissic Complex_percent": properties.get( + "principle_aq_Banded Gneissic Complex_percent", "0" + ), + "principle_aq_Basalt_percent": properties.get( + "principle_aq_Basalt_percent", "0" + ), + "principle_aq_Charnockite_percent": properties.get( + "principle_aq_Charnockite_percent", "0" + ), + "principle_aq_Gneiss_percent": properties.get( + "principle_aq_Gneiss_percent", "0" + ), + "principle_aq_Granite_percent": properties.get( + "principle_aq_Granite_percent", "0" + ), + "principle_aq_Intrusive_percent": properties.get( + "principle_aq_Intrusive_percent", "0" + ), + "principle_aq_Khondalite_percent": properties.get( + "principle_aq_Khondalite_percent", "0" + ), + "principle_aq_Laterite_percent": properties.get( + "principle_aq_Laterite_percent", "0" + ), + "principle_aq_Limestone_percent": properties.get( + "principle_aq_Limestone_percent", "0" + ), + "principle_aq_Quartzite_percent": properties.get( + "principle_aq_Quartzite_percent", "0" + ), + "principle_aq_Sandstone_percent": properties.get( + "principle_aq_Sandstone_percent", "0" + ), + "principle_aq_Schist_percent": properties.get( + "principle_aq_Schist_percent", "0" + ), + "principle_aq_Shale_percent": properties.get( + "principle_aq_Shale_percent", "0" + ), + "principle_aq_None_percent": properties.get( + "principle_aq_None_percent", "0" + ), + } + + df_data.append(row) + + df = pd.DataFrame(df_data) + # Round numeric values + numeric_cols = df.select_dtypes(include=["number"]).columns + df[numeric_cols] = df[numeric_cols].round(2) + df = df.sort_values(["UID"]) + df.to_excel(writer, sheet_name="aquifer_vector", index=False) + print("Excel file created for aquifer_vector") + + +def create_excel_for_restoration(data, xlsx_file, writer): + df_data = [] + features = data["features"] + + for feature in features: + properties = feature["properties"] + row = { + "UID": properties["uid"], + "area_in_ha": properties["area_in_ha"], + "wide_scale_restoration_area_in_ha": properties["Wide-scale"], + "protection_area_in_ha": properties["Protection"], + "mosaic_restoration_area_in_ha": properties["Mosaic Res"], + "excluded_areas_in_ha": properties["Excluded A"], + } + + df_data.append(row) + df = pd.DataFrame(df_data) + df = df.sort_values(["UID"]) + + ## for roundoff all numeric value upto 2 decimal + numeric_cols = df.select_dtypes(include=["int64", "float64"]).columns + df[numeric_cols] = df[numeric_cols].round(2) + + df.to_excel(writer, sheet_name="restoration_vector", index=False) + print(f"Excel file created for restoration_vector") + + +def create_excel_for_overall_tree_change(data, xlsx_file, writer): + df_data = [] + features = data["features"] + + for feature in features: + properties = feature["properties"] + row = { + "UID": properties["uid"], + "area_in_ha": properties["area_in_ha"], + "afforestation_area_in_ha": properties["Afforestation"], + "deforestation_area_in_ha": properties["Deforestation"], + "degradation_area_in_ha": properties["Degradation"], + "improvement_area_in_ha": properties["Improvement"], + "missing_data_in_ha": properties["Missing Data"], + "no_change_area_in_ha": properties["No_Change"], + "partially_degraded_area_in_ha": properties["Partially_Degraded"], + } + + df_data.append(row) + df = pd.DataFrame(df_data) + df = df.sort_values(["UID"]) + + ## for roundoff all numeric value upto 2 decimal + numeric_cols = df.select_dtypes(include=["int64", "float64"]).columns + df[numeric_cols] = df[numeric_cols].round(2) + + df.to_excel(writer, sheet_name="overall_tree_change", index=False) + print(f"Excel file created for overall_tree_change") + + +def create_excel_for_ccd(data, xlsx_file, writer, start_year, end_year): + df_data = [] + features = data["features"] + for feature in features: + properties = feature["properties"] + + row = { + "UID": properties["uid"], + "area_in_ha": properties["area_in_ha"], + } + + for year in range(start_year, end_year + 1): + row["high_density_area_in_ha_" + str(year)] = properties.get( + "High_Density_" + str(year), None + ) + row["low_density_area_in_ha_" + str(year)] = properties.get( + "Low_Density_" + str(year), None + ) + row["missing_data_area_in_ha_" + str(year)] = properties.get( + "Missing_Data_" + str(year), None + ) + + df_data.append(row) + + df = pd.DataFrame(df_data) + df = df.sort_values(["UID"]) + + ## for roundoff all numeric value upto 2 decimal + numeric_cols = df.select_dtypes(include=["int64", "float64"]).columns + df[numeric_cols] = df[numeric_cols].round(2) + + df.to_excel(writer, sheet_name="Canopy_Cover_Density", index=False) + print(f"Excel file created for Canopy_Cover_Density") + + +def create_excel_for_ch(data, xlsx_file, writer, start_year, end_year): + df_data = [] + features = data["features"] + for feature in features: + properties = feature["properties"] + + row = { + "UID": properties["uid"], + "area_in_ha": properties["area_in_ha"], + } + + for year in range(start_year, end_year + 1): + row["short_trees_area_in_ha_" + str(year)] = properties.get( + "Short_Trees_" + str(year), None + ) + row["medium_trees_area_in_ha_" + str(year)] = properties.get( + "Medium_Height_Trees_" + str(year), None + ) + row["tall_trees_area_in_ha_" + str(year)] = properties.get( + "Tall_Trees_" + str(year), None + ) + row["missing_data_area_in_ha_" + str(year)] = properties.get( + "Missing_Data_" + str(year), None + ) + + df_data.append(row) + + df = pd.DataFrame(df_data) + df = df.sort_values(["UID"]) + + ## for roundoff all numeric value upto 2 decimal + numeric_cols = df.select_dtypes(include=["int64", "float64"]).columns + df[numeric_cols] = df[numeric_cols].round(2) + + df.to_excel(writer, sheet_name="Canopy_height", index=False) + print(f"Excel file created for Canopy_height") + + +def create_excel_for_drought_causality(data, xlsx_file, writer, start_year, end_year): + df_data = [] + features = data["features"] + for feature in features: + properties = feature["properties"] + + row = { + "UID": properties["uid"], + "area_in_ha": properties["area_in_ha"], + } + + for year in range(start_year, end_year + 1): + row["severe_moderate_drought_causality_" + str(year)] = properties.get( + "se_mo_" + str(year), None + ) + row["mild_drought_causality_" + str(year)] = properties.get( + "mild_" + str(year), None + ) + + df_data.append(row) + + df = pd.DataFrame(df_data) + df = df.sort_values(["UID"]) + + ## for roundoff all numeric value upto 2 decimal + numeric_cols = df.select_dtypes(include=["int64", "float64"]).columns + df[numeric_cols] = df[numeric_cols].round(2) + + df.to_excel(writer, sheet_name="drought_causality", index=False) + print(f"Excel file created for drought_causality") + + +def create_excel_chan_detection_afforestation(data, xlsx_file, writer): + df_data = [] + features = data["features"] + + for feature in features: + properties = feature["properties"] + uid = properties.get("uid", "Unknown") + df_data.append( + { + "UID": uid, + "area_in_ha": properties.get("area_in_ha", None), + "barren_to_forest_area_in_ha": properties.get("ba_fo", None), + "built_up_to_forest_area_in_ha": properties.get("bu_fo", None), + "farm_to_forest_area_in_ha": properties.get("fa_fo", None), + "forest_to_forest_area_in_ha": properties.get("fo_fo", None), + "scrub_land_to_forest_area_in_ha": properties.get("sc_fo", None), + "total_afforestation_area_in_ha": properties.get("total_aff", None), + } + ) + + df = pd.DataFrame(df_data) + df = df.sort_values(["UID"]) + + ## for roundoff all numeric value upto 2 decimal + numeric_cols = df.select_dtypes(include=["int64", "float64"]).columns + df[numeric_cols] = df[numeric_cols].round(2) + + df.to_excel(writer, sheet_name="change_detection_afforestation", index=False) + print(f"Excel file created for change_detection_afforestation") + + +def create_excel_chan_detection_cropintensity(data, xlsx_file, writer): + df_data = [] + features = data["features"] + + for feature in features: + properties = feature["properties"] + uid = properties.get("uid", "Unknown") + df_data.append( + { + "UID": uid, + "area_in_ha": properties.get("area_in_ha", None), + "single_to_single_area_in_ha": properties.get("si_si", None), + "single_to_double_area_in_ha": properties.get("si_do", None), + "single_to_triple_area_in_ha": properties.get("si_tr", None), + "double_to_single_area_in_ha": properties.get("do_si", None), + "double_to_double_area_in_ha": properties.get("do_do", None), + "double_to_triple_area_in_ha": properties.get("do_tr", None), + "triple_to_single_area_in_ha": properties.get("tr_si", None), + "triple_to_double_area_in_ha": properties.get("tr_do", None), + "triple_to_triple_area_in_ha": properties.get("tr_tr", None), + "total_change_crop_intensity_area_in_ha": properties.get( + "total_chan", properties.get("total_change", None) + ), + } + ) + + df = pd.DataFrame(df_data) + df = df.sort_values(["UID"]) + + ## for roundoff all numeric value upto 2 decimal + numeric_cols = df.select_dtypes(include=["int64", "float64"]).columns + df[numeric_cols] = df[numeric_cols].round(2) + + df.to_excel(writer, sheet_name="change_detection_cropintensity", index=False) + print(f"Excel file created for change_detection_cropintensity") + + +def create_excel_chan_detection_deforestation(data, xlsx_file, writer): + df_data = [] + features = data["features"] + + for feature in features: + properties = feature["properties"] + uid = properties.get("uid", "Unknown") + df_data.append( + { + "UID": uid, + "area_in_ha": properties.get("area_in_ha", None), + "forest_to_barren_area_in_ha": properties.get("fo_ba", None), + "forest_to_built_up_area_in_ha": properties.get("fo_bu", None), + "forest_to_farm_area_in_ha": properties.get("fo_fa", None), + "forest_to_forest_area_in_ha": properties.get("fo_fo", None), + "forest_to_scrub_land_area_in_ha": properties.get("fo_sc", None), + "total_deforestation_area_in_ha": properties.get("total_def", None), + } + ) + + df = pd.DataFrame(df_data) + df = df.sort_values(["UID"]) + + ## for roundoff all numeric value upto 2 decimal + numeric_cols = df.select_dtypes(include=["int64", "float64"]).columns + df[numeric_cols] = df[numeric_cols].round(2) + + df.to_excel(writer, sheet_name="change_detection_deforestation", index=False) + print(f"Excel file created for change_detection_deforestation") + + +def create_excel_chan_detection_degradation(data, xlsx_file, writer): + df_data = [] + features = data["features"] + + for feature in features: + properties = feature["properties"] + uid = properties.get("uid", "Unknown") + df_data.append( + { + "UID": uid, + "area_in_ha": properties.get("area_in_ha", None), + "farm_to_barren_area_in_ha": properties.get("f_ba", None), + "farm_to_built_up_area_in_ha": properties.get("f_bu", None), + "farm_to_farm_area_in_ha": properties.get("f_f", None), + "farm_to_scrub_land_area_in_ha": properties.get("f_sc", None), + "total_degradation_area_in_ha": properties.get("total_deg", None), + } + ) + + df = pd.DataFrame(df_data) + df = df.sort_values(["UID"]) + + ## for roundoff all numeric value upto 2 decimal + numeric_cols = df.select_dtypes(include=["int64", "float64"]).columns + df[numeric_cols] = df[numeric_cols].round(2) + + df.to_excel(writer, sheet_name="change_detection_degradation", index=False) + print(f"Excel file created for change_detection_degradation") + + +def create_excel_chan_detection_urbanization(data, xlsx_file, writer): + df_data = [] + features = data["features"] + + for feature in features: + properties = feature["properties"] + uid = properties.get("uid", "Unknown") + df_data.append( + { + "UID": uid, + "area_in_ha": properties.get("area_in_ha", None), + "barren_shrub_to_built_up_area_in_ha": properties.get("b_bu", None), + "built_up_to_built_up_area_in_ha": properties.get("bu_bu", None), + "tree_farm_to_built_up_area_in_ha": properties.get("tr_bu", None), + "water_to_built_up_area_in_ha": properties.get("w_bu", None), + "total_urbanization_area_in_ha": properties.get("total_urb", None), + } + ) + + df = pd.DataFrame(df_data) + df = df.sort_values(["UID"]) + + ## for roundoff all numeric value upto 2 decimal + numeric_cols = df.select_dtypes(include=["int64", "float64"]).columns + df[numeric_cols] = df[numeric_cols].round(2) + + df.to_excel(writer, sheet_name="change_detection_urbanization", index=False) + print(f"Excel file created for change_detection_urbanization") + + +def create_excel_mws_inters_villages(mws_geojson, xlsx_file, writer, district, block): + print("Inside create_excel_mws_inters_villages") + admin_layer_name = district + "_" + block + admin_file_url = get_url("panchayat_boundaries", admin_layer_name) + + response = requests.get(admin_file_url) + if response.status_code != 200: + print(f"Error fetching data: {response.status_code}") + return + + village_geojson = response.json() + + def calculate_intersection_area_ha(village_geom, mws_geom, mws_area_ha): + if village_geom.intersects(mws_geom): + intersection = village_geom.intersection(mws_geom) + if mws_geom.area == 0: + return 0 + # ratio of intersection to full MWS geometry area (both in degrees²) + ratio = intersection.area / mws_geom.area + return ratio * mws_area_ha + return 0 + + mws_villages_dict = {} + + for mws_feature in mws_geojson["features"]: + mws_uid = mws_feature["properties"]["uid"] + mws_area = mws_feature["properties"]["area_in_ha"] + mws_geom = shape(mws_feature["geometry"]) + village_ids = set() + village_details = {} + + for village_feature in village_geojson["features"]: + village_id = village_feature["properties"]["vill_ID"] + if village_id == 0: + continue + + village_geom = shape(village_feature["geometry"]) + area_intersected_ha = calculate_intersection_area_ha( + village_geom, mws_geom, mws_area + ) + if area_intersected_ha > 0: + village_ids.add(village_id) + percentage_of_area = ( + (area_intersected_ha / mws_area) * 100 if mws_area > 0 else 0 + ) + village_details[village_id] = { + "area_intersect": round(area_intersected_ha, 2), + "percentage_of_area": round(percentage_of_area, 2), + } + + if village_ids: + mws_villages_dict[mws_uid] = { + "area_in_ha": mws_area, + "village_ids": list(village_ids), + "village_details": village_details, + } + + data = [ + { + "MWS UID": mws_uid, + "area_in_ha": values["area_in_ha"], + "Village IDs": values["village_ids"], + "Village Details": values["village_details"], + } + for mws_uid, values in mws_villages_dict.items() + ] + + df = pd.DataFrame(data) + numeric_cols = df.select_dtypes(include=["int64", "float64"]).columns + df[numeric_cols] = df[numeric_cols].round(2) + df.to_excel(writer, sheet_name="mws_intersect_villages", index=False) + print("The data has been saved to mws_intersect_villages") + + +# def create_excel_village_inters_mwss(mws_geojson, xlsx_file, writer, district, block): +# print("Inside create_excel_village_inters_mwss") +# admin_layer_name = district + '_' + block +# admin_file_url = get_url('panchayat_boundaries', admin_layer_name) + +# response = requests.get(admin_file_url) +# if response.status_code != 200: +# print(f"Error fetching data: {response.status_code}") +# return +# village_geojson = response.json() + +# def calculate_intersection_area(village_geom: BaseGeometry, mws_geom: BaseGeometry) : +# try: +# # Check for empty geometries +# if village_geom.is_empty or mws_geom.is_empty: +# return 0.0 + +# # Fix invalid geometries if needed +# if not village_geom.is_valid: +# village_geom = village_geom.buffer(0) +# if not mws_geom.is_valid: +# mws_geom = mws_geom.buffer(0) + +# # Calculate intersection +# if village_geom.intersects(mws_geom): +# intersection = village_geom.intersection(mws_geom) +# return intersection.area if not intersection.is_empty else 0.0 + +# return 0.0 + +# except Exception as e: +# print(f"Error calculating intersection area: {e}") +# return 0.0 + + +# data = [] + +# processed_villages = set() + +# for village_feature in village_geojson['features']: +# village_id = village_feature['properties']['vill_ID'] +# village_name = village_feature['properties']['vill_name'] + +# village_key = (village_id, village_name) + +# if village_key in processed_villages: +# continue +# processed_villages.add(village_key) + +# village_geom = shape(village_feature['geometry']) + +# mws_uids = [] +# intersection_areas = [] + +# for mws_feature in mws_geojson['features']: +# mws_geom = shape(mws_feature['geometry']) +# area_intersected = calculate_intersection_area(village_geom, mws_geom) +# if area_intersected > 0: +# mws_uids.append(mws_feature['properties']['uid']) +# intersection_areas.append(area_intersected) + +# data.append({ +# 'Village ID': village_id, +# 'Village Name': village_name, +# 'MWS UIDs': mws_uids, +# }) + +# df = pd.DataFrame(data) + +# ## for roundoff all numeric value upto 2 decimal +# numeric_cols = df.select_dtypes(include=['int64', 'float64']).columns +# df[numeric_cols] = df[numeric_cols].round(2) + +# df.to_excel(writer, sheet_name='village_intersect_mwss', index=False) +# print("Excel created for village_intersect_mwss") + + +def create_excel_for_terrain(data, output_file, writer): + print("Inside create_excel_for_terrain function") + df_data = [] + + terrain_description = { + 0: "Broad Sloppy and Hilly", + 1: "Mostly Plains", + 2: "Mostly Hills and Valleys", + 3: "Broad Plains and Slopes", + } + + features = data["features"] + + for feature in features: + properties = feature["properties"] + row = { + "UID": properties["uid"], + "area_in_ha": properties["area_in_ha"], + "terrain_cluster_id": properties["terrainClu"], + "terrain_description": terrain_description.get(properties["terrainClu"]), + "hill_slope_area_percent": properties["hill_slope"], + "plain_area_percent": properties["plain_area"], + "ridge_area_percent": properties["ridge_area"], + "slopy_area_percent": properties["slopy_area"], + "valley_area_percent": properties["valley_are"], + } + + df_data.append(row) + + df = pd.DataFrame(df_data) + df = df.sort_values(["UID"]) + + ## for roundoff all numeric value upto 2 decimal + numeric_cols = df.select_dtypes(include=["int64", "float64"]).columns + df[numeric_cols] = df[numeric_cols].round(2) + + df.to_excel(writer, sheet_name="terrain", index=False) + print(f"Excel file created for terrain vector") + + +def create_excel_for_terrain_lulc_slope(data, output_file, writer): + df_data = [] + + terrain_description = { + 0: "Broad Sloppy and Hilly", + 1: "Mostly Plains", + 2: "Mostly Hills and Valleys", + 3: "Broad Plains and Slopes", + } + + features = data["features"] + + for feature in features: + properties = feature["properties"] + row = { + "UID": properties["uid"], + "area_in_ha": properties["area_in_ha"], + "terrain_cluster_id": properties["terrain_cl"], + "terrain_description": terrain_description.get(properties["terrain_cl"]), + "cluster_name": properties["clust_name"], + "barren_area_percent": properties["barren"], + "forests_area_percent": properties["forests"], + "shrub_scrubs_area_percent": properties["shrub_scru"], + "single_kharif_area_percent": properties["sing_khari"], + "single_non_kharif_area_percent": properties["sing_non_k"], + "double_cropping_area_percent": properties["double"], + "triple_cropping_area_percent": properties["triple"], + } + + df_data.append(row) + df = pd.DataFrame(df_data) + df = df.sort_values(["UID"]) + + ## for roundoff all numeric value upto 2 decimal + numeric_cols = df.select_dtypes(include=["int64", "float64"]).columns + df[numeric_cols] = df[numeric_cols].round(2) + + df.to_excel(writer, sheet_name="terrain_lulc_slope", index=False) + print("Excel file created for terrain_lulc_slope") + + +def create_excel_for_terrain_lulc_plain(data, output_file, writer): + df_data = [] + + terrain_description = { + 0: "Broad Sloppy and Hilly", + 1: "Mostly Plains", + 2: "Mostly Hills and Valleys", + 3: "Broad Plains and Slopes", + } + + features = data["features"] + + for feature in features: + properties = feature["properties"] + row = { + "UID": properties["uid"], + "area_in_ha": properties["area_in_ha"], + "terrain_cluster_id": properties["terrain_cl"], + "terrain_description": terrain_description.get(properties["terrain_cl"]), + "cluster_name": properties["clust_name"], + "barren_area_percent": properties["barren"], + "forests_area_percent": properties["forest"], + "shrub_scrubs_area_percent": properties["shrubs_scr"], + "single_non_kharif_area_percent": properties["sing_non_k"], + "single_kharif_area_percent": properties["sing_crop"], + "double_cropping_area_percent": properties["double_cro"], + "triple_cropping_area_percent": properties["triple_cro"], + } + + df_data.append(row) + + df = pd.DataFrame(df_data) + df = df.sort_values(["UID"]) + + numeric_cols = df.select_dtypes(include=["int64", "float64"]).columns + df[numeric_cols] = df[numeric_cols].round(2) + + df.to_excel(writer, sheet_name="terrain_lulc_plain", index=False) + print("Excel file created for terrain_lulc_plain") + + +def create_excel_for_swb(data, output_file, writer, start_year, end_year): + df_data = [] + features = data.get("features", []) + + for feature in features: + properties = feature.get("properties", {}) + uid = properties.get("MWS_UID", "Unknown") + + def calculate_area(base_area, percentage): + if base_area == 0 or percentage == 0: + return 0 + return base_area * (percentage / 100) + + parts = uid.split("_") + num_uid_parts_is = [ + f"{parts[i]}_{parts[i + 1]}" for i in range(0, len(parts) - 1, 2) + ] + if len(parts) % 2 == 1: # Check for an unpaired last part + num_uid_parts_is.append(parts[-1]) + + # Generate years dynamically based on start_year and end_year + years = range(start_year, end_year) + + for num_uid_part in num_uid_parts_is: + row = {"UID": num_uid_part} + + for year in years: + short_year = f"{str(year)[-2:]}-{str(year + 1)[-2:]}" + + # Construct keys dynamically using the shortened year format + total_area_key = f"area_{short_year}" + kharif_key = f"k_{short_year}" + rabi_key = f"kr_{short_year}" + zaid_key = f"krz_{short_year}" + + # Get values from properties + total_area = properties.get(total_area_key, 0) + kharif_percentage = properties.get(kharif_key, 0) + rabi_percentage = properties.get(rabi_key, 0) + zaid_percentage = properties.get(zaid_key, 0) + + # Calculate areas + row[f"total_area_in_ha_{year}-{year + 1}"] = total_area / len( + num_uid_parts_is + ) + row[f"kharif_area_in_ha_{year}-{year + 1}"] = calculate_area( + total_area, kharif_percentage + ) / len(num_uid_parts_is) + row[f"rabi_area_in_ha_{year}-{year + 1}"] = calculate_area( + total_area, rabi_percentage + ) / len(num_uid_parts_is) + row[f"zaid_area_in_ha_{year}-{year + 1}"] = calculate_area( + total_area, zaid_percentage + ) / len(num_uid_parts_is) + + # Add total SWB area + row["total_swb_area_in_ha"] = properties.get("area_ored", 0) / len( + num_uid_parts_is + ) + df_data.append(row) + + df = pd.DataFrame(df_data) + agg_dict = {col: "sum" for col in df.columns if col != "UID"} + grouped_df = df.groupby("UID").agg(agg_dict).reset_index() + + df = grouped_df.sort_values(["UID"]) + + numeric_cols = df.select_dtypes(include=["int64", "float64"]).columns + df[numeric_cols] = df[numeric_cols].round(2) + + df.to_excel(writer, sheet_name="surfaceWaterBodies_annual", index=False) + print("Excel file created for surfaceWaterBodies_annual") + + +def create_excel_for_nrega_assets( + nrega_data, mws_data, output_file, writer, start_year, end_year +): + workCategoryMapping = { + "SWC - Landscape level impact": "Soil and water conservation", + "Agri Impact - HH, Community": "Land restoration", + "Plantation": "Plantations", + "Irrigation - Site level impact": "Irrigation on farms", + "Irrigation Site level - Non RWH": "Other farm works", + "Household Livelihood": "Off-farm livelihood assets", + "Others - HH, Community": "Community assets", + } + + mws = gpd.GeoDataFrame.from_features(mws_data["features"]) + nrega = gpd.GeoDataFrame.from_features(nrega_data["features"]) + + # Set CRS if available in JSON + if "crs" in mws_data: + mws.set_crs(mws_data["crs"]["properties"]["name"], inplace=True) + if "crs" in nrega_data: + nrega.set_crs(nrega_data["crs"]["properties"]["name"], inplace=True) + + joined = gpd.sjoin(nrega, mws, how="inner", predicate="within") + counts = {} + + df_data = [] + valid_years = range(start_year, end_year) + + date_formats = [ + "%d-%b-%y %H:%M:%S.%f", + "%d-%b-%y %H:%M:%S", + "%d-%m-%y %H:%M:%S.%f", + "%d-%m-%y %H:%M:%S", + "%d-%b-%Y %H:%M:%S.%f", + "%d-%b-%Y %H:%M:%S", + "%d-%m-%Y %H:%M:%S.%f", + "%d-%m-%Y %H:%M:%S", + "%Y-%m-%d %H:%M:%S.%f", + "%Y-%m-%d %H:%M:%S", + "%Y/%m/%d %H:%M:%S.%f", + "%Y/%m/%d %H:%M:%S", + "%Y-%m-%dT%H:%M:%SZ", + ] + + for _, row in joined.iterrows(): + creation_t = row["creation_t"] + work_category = row["WorkCatego"] + mws_id = row["uid"] + + if isinstance(creation_t, pd.Timestamp): + creation_t = creation_t.strftime("%d-%m-%Y %H:%M:%S") + + date_obj = None + for date_format in date_formats: + try: + date_obj = datetime.strptime(creation_t, date_format) + break + except ValueError: + continue + + if date_obj is None: + continue + + year = date_obj.year + if year < 100: + year += 2000 + + if year not in valid_years: + continue + + category = workCategoryMapping.get(work_category, "Others - HH, Community") + + if mws_id not in counts: + counts[mws_id] = { + year: {cat: 0 for cat in workCategoryMapping.values()} + for year in range(start_year, end_year) + } + + if category not in counts[mws_id][year]: + counts[mws_id][year][category] = 0 + counts[mws_id][year][category] += 1 + + for mws_id, year_data in counts.items(): + row = {"mws_id": mws_id} + for year, categories in year_data.items(): + for category in workCategoryMapping.values(): + count = categories.get(category, 0) + row[f"{category}_count_{year}"] = count + df_data.append(row) + + if not df_data: + print("No data was collected for the DataFrame.") + else: + print(f"Collected {len(df_data)} rows of data for the DataFrame.") + + if df_data: + df = pd.DataFrame(df_data) + df.to_excel(writer, sheet_name="nrega_annual", index=False) + print("Excel file created for nrega_annual") + return "successfully created" + else: + print("No data available to write to Excel.") + + +def create_excel_village_nrega_assets( + result_df, output_file, writer, all_villages_df, start_year, end_year +): + workCategoryMapping = { + "SWC - Landscape level impact": "Soil and water conservation", + "Agri Impact - HH, Community": "Land restoration", + "Plantation": "Plantations", + "Irrigation - Site level impact": "Irrigation on farms", + "Irrigation Site level - Non RWH": "Other farm works", + "Household Livelihood": "Off-farm livelihood assets", + "Others - HH, Community": "Community assets", + } + + # start_year, end_year = 2017, 2022 + year_range = range(start_year, end_year + 1) + + # Initialize all-zero DataFrame for all villages + rows = [] + for _, row in all_villages_df.iterrows(): + base_row = {"vill_id": row["vill_ID"], "vill_name": row["vill_name"]} + for year in year_range: + for cat in workCategoryMapping.values(): + base_row[f"{cat}_count_{year}"] = 0 + rows.append(base_row) + + final_df = pd.DataFrame(rows) + + # Fill counts from assets + for _, row in result_df.iterrows(): + creation_t = row["creation_t"] + try: + date_obj = pd.to_datetime(creation_t, errors="coerce") + if pd.isnull(date_obj): + continue + except: + continue + + year = date_obj.year + if year not in year_range: + continue + + category = workCategoryMapping.get(row["WorkCatego"]) + if not category: + continue + + mask = (final_df["vill_id"] == row["vill_ID"]) & ( + final_df["vill_name"] == row["vill_name"] + ) + col_name = f"{category}_count_{year}" + final_df.loc[mask, col_name] += 1 + + # Sort columns for clean layout + id_cols = ["vill_id", "vill_name"] + category_cols = sorted( + [col for col in final_df.columns if col not in id_cols], + key=lambda x: (int(x.split("_")[-1]), x), + ) + final_df = final_df[id_cols + category_cols] + + # Save to Excel + final_df = final_df.drop_duplicates(subset=["vill_id", "vill_name"]) + final_df.to_excel(writer, sheet_name="nrega_assets_village", index=False) + print("Excel file created successfully with all villages.") + + +def fetch_village_asset_count( + state, district, block, writer, output_file, start_year, end_year +): + # 1. Read village data + village_gdf = gpd.read_file(get_url("panchayat_boundaries", f"{district}_{block}"))[ + ["vill_ID", "vill_name", "geometry"] + ].copy() + print("Village data loaded") + + # 2. Get NREGA data with timeout to prevent hanging + try: + nrega_url = get_url("nrega_assets", f"{district}_{block}") + print(f"Fetching: {nrega_url}") + + # Add timeout to prevent hanging + response = requests.get(nrega_url, timeout=120) + nrega_json = response.json() + print("NREGA data fetched successfully") + + except Exception as e: + print(f"Failed to get NREGA data: {e}") + # Return villages with "No Asset" if API fails + result_df = village_gdf[["vill_ID", "vill_name"]].copy() + result_df["Asset ID"] = "No Asset" + result_df["creation_t"] = "No Asset" + result_df["WorkCatego"] = "No Asset" + create_excel_village_nrega_assets( + result_df, + output_file, + writer, + village_gdf[["vill_ID", "vill_name"]], + start_year, + end_year, + ) + return result_df, village_gdf[["vill_ID", "vill_name"]] + + # 3. Process asset features + points_data = [] + features = nrega_json.get("features", []) + print(f"Processing {len(features)} features") + + for feature in features: + try: + point = Point(feature["geometry"]["coordinates"]) + properties = feature["properties"] + points_data.append( + { + "geometry": point, + "Asset ID": properties.get("Asset ID", "MISSING"), + "creation_t": properties.get("creation_t", ""), + "WorkCatego": properties.get("WorkCatego", ""), + } + ) + except: + continue + + # If no valid points, return no assets + if not points_data: + print("No valid asset points found") + result_df = village_gdf[["vill_ID", "vill_name"]].copy() + result_df["Asset ID"] = "No Asset" + result_df["creation_t"] = "No Asset" + result_df["WorkCatego"] = "No Asset" + create_excel_village_nrega_assets( + result_df, + output_file, + writer, + village_gdf[["vill_ID", "vill_name"]], + start_year, + end_year, + ) + return result_df, village_gdf[["vill_ID", "vill_name"]] + + print("Asset points created") + + # 4. Create points GeoDataFrame and match CRS + points_gdf = gpd.GeoDataFrame(points_data, geometry="geometry") + if village_gdf.crs != points_gdf.crs: + points_gdf.set_crs(village_gdf.crs, inplace=True) + + # 5. Find which village each asset belongs to + joined_gdf = gpd.sjoin(points_gdf, village_gdf, how="inner", predicate="within") + + # 6. Get asset + village info + result_df = joined_gdf[ + ["vill_ID", "vill_name", "Asset ID", "creation_t", "WorkCatego"] + ].copy() + + # 7. Add villages that have no assets + villages_with_assets = result_df["vill_ID"].unique() + no_asset_villages = village_gdf[ + ~village_gdf["vill_ID"].isin(villages_with_assets) + ].copy() + no_asset_villages["Asset ID"] = "No Asset" + no_asset_villages["creation_t"] = "No Asset" + no_asset_villages["WorkCatego"] = "No Asset" + + result_df = pd.concat( + [ + result_df, + no_asset_villages[ + ["vill_ID", "vill_name", "Asset ID", "creation_t", "WorkCatego"] + ], + ], + ignore_index=True, + ) + + print("Processing complete") + + # 8. Create Excel file + create_excel_village_nrega_assets( + result_df, + output_file, + writer, + village_gdf[["vill_ID", "vill_name"]], + start_year, + end_year, + ) + + return result_df, village_gdf[["vill_ID", "vill_name"]] + + +def analyze_results(village_asset_count, village_gdf): + villages_with_counts = village_gdf.merge( + village_asset_count, on="vill_ID", how="left" + ) + villages_with_counts["asset_count"] = villages_with_counts["asset_count"].fillna(0) + return villages_with_counts + + +def create_excel_crop_inten(data, output_file, writer, start_year, end_year): + df_data = [] + + features = data["features"] + for feature in features: + properties = feature.get("properties", {}) + uid = properties.get("uid", "Unknown") + row = {"UID": uid, "area_in_ha": properties.get("area_in_ha", 0)} + + # Process each year in range using new key naming convention + for year in range(start_year, end_year + 1): + cropping_key = f"cropping_intensity_{year}" + single_c_key = f"single_cropped_area_{year}" + single_k_key = f"single_kharif_cropped_area_{year}" + single_n_key = f"single_non_kharif_cropped_area_{year}" + doubly_c_key = f"doubly_cropped_area_{year}" + triply_c_key = f"triply_cropped_area_{year}" + + row[f"cropping_intensity_unit_less_{year}-{year + 1}"] = properties.get( + cropping_key, 0 + ) + row[f"single_cropped_area_in_ha_{year}-{year + 1}"] = properties.get( + single_c_key, 0 + ) + row[f"single_kharif_cropped_area_in_ha_{year}-{year + 1}"] = properties.get( + single_k_key, 0 + ) + row[f"single_non_kharif_cropped_area_in_ha_{year}-{year + 1}"] = ( + properties.get(single_n_key, 0) + ) + row[f"doubly_cropped_area_in_ha_{year}-{year + 1}"] = properties.get( + doubly_c_key, 0 + ) + row[f"triply_cropped_area_in_ha_{year}-{year + 1}"] = properties.get( + triply_c_key, 0 + ) + + row["sum_area_in_ha"] = properties.get("sum", 0) / 10000 + df_data.append(row) + + # Create and format DataFrame + df = pd.DataFrame(df_data) + df = df.sort_values(["UID"]) + + # Round numeric columns + numeric_cols = df.select_dtypes(include=["int64", "float64"]).columns + df[numeric_cols] = df[numeric_cols].round(2) + + # Write to Excel + df.to_excel(writer, sheet_name="croppingIntensity_annual", index=False) + print("Excel file created for cropping intensity.") + + +def create_excel_crop_drou(data, output_file, writer, start_year, end_year): + df_data = [] + + features = data["features"] + for feature in features: + properties = feature.get("properties", {}) + row = {"UID": properties.get("uid", "Unknown ID")} + + for year in range(start_year, end_year + 1): + drlb_key = f"drlb_{year}" + drysp_key = f"drysp_{year}" + kh_cr_key = f"kh_cr_{year}" + m_ons_key = f"m_ons_{year}" + pcr_k_key = f"pcr_k_{year}" + t_wks_key = f"t_wks_{year}" + + # Get drought levels (drlb) and count occurrences + drlb_value = properties.get(drlb_key, "") + row[f"No_Drought_in_weeks_{year}"] = drlb_value.count("0") + row[f"Mild_in_weeks_{year}"] = drlb_value.count("1") + row[f"Moderate_in_weeks_{year}"] = drlb_value.count("2") + row[f"Severe_in_weeks_{year}"] = drlb_value.count("3") + + # Add other properties + row[f"drysp_unit_4_weeks_{year}"] = properties.get(drysp_key, "0") + row[f"kharif_cropped_sqkm_{year}"] = properties.get(kh_cr_key, "0") + row[f"monsoon_onset_{year}"] = properties.get(m_ons_key, "0") + row[f"kharif_cropped_area_percent_{year}"] = properties.get(pcr_k_key, "0") + row[f"total_weeks_{year}"] = properties.get(t_wks_key, "0") + + df_data.append(row) + + # Create DataFrame + df = pd.DataFrame(df_data) + df = df.sort_values(["UID"]) + + ## for roundoff all numeric value upto 2 decimal + numeric_cols = df.select_dtypes(include=["int64", "float64"]).columns + df[numeric_cols] = df[numeric_cols].round(2) + + # Write to Excel + df.to_excel(writer, sheet_name="croppingDrought_kharif", index=False) + print("Excel file created for cropping drought.") + + +def parse_geojson_annual_mws(data): + features = data["features"] + + all_data = defaultdict(lambda: defaultdict(dict)) + + for feature in features: + properties = feature["properties"] + uid = properties.get("uid", "Unknown") + + for key, value in properties.items(): + if isinstance(key, str) and isinstance(value, str): + if key.startswith("20") and len(key) == 9: + year = key + try: + # Attempt to parse the value as JSON + year_data = json.loads(value.replace("'", '"')) + all_data[uid][year] = year_data + except Exception as e: + print(f"Couldn't parse data for {uid}, {key}: {e}") + + return all_data + + +def create_excel_annual_mws(data, output_file, writer): + df_data = [] + year_columns = ["ET", "RunOff", "G", "DeltaG", "Precipitation", "WellDepth"] + + for uid, years in data.items(): + row = {"UID": uid} + + for year, metrics in years.items(): + start_year = year[:4] + end_year = str(int(start_year) + 1) + formatted_year = f"{start_year}-{end_year}" + + for col in year_columns: + if col == "WellDepth": + column_name = f"{col}_in_m_{formatted_year}" + row[column_name] = metrics.get(col, "N/A") + else: + column_name = f"{col}_in_mm_{formatted_year}" + row[column_name] = metrics.get(col, "N/A") + + df_data.append(row) + + df = pd.DataFrame(df_data) + df = df.sort_values(["UID"]) + + ## for roundoff all numeric value upto 2 decimal + numeric_cols = df.select_dtypes(include=["int64", "float64"]).columns + df[numeric_cols] = df[numeric_cols].round(2) + + df.to_excel(writer, sheet_name="hydrological_annual", index=False) + print("Excel file created for hydrological_annual") + + +def parse_json_seas_mws(file_path): + with open(file_path, "r") as file: + data = json.load(file) + return data + + +def get_season(month): + if month in (3, 4, 5, 6): + return "zaid" + elif month in (7, 8, 9, 10): + return "kharif" + elif month in (11, 12, 1, 2): + return "rabi" + + +def process_feature(feature): + uid = feature["properties"]["uid"] + results = { + "UID": uid, + "precipitation": {"kharif": {}, "rabi": {}, "zaid": {}}, + "et": {"kharif": {}, "rabi": {}, "zaid": {}}, + "runoff": {"kharif": {}, "rabi": {}, "zaid": {}}, + "delta g": {"kharif": {}, "rabi": {}, "zaid": {}}, + "g": {"kharif": {}, "rabi": {}, "zaid": {}}, + } + + variable_mapping = { + "Precipitation": "precipitation", + "ET": "et", + "RunOff": "runoff", + "DeltaG": "delta g", + "G": "g", + } + + for key, value in feature["properties"].items(): + if key.startswith("20"): + try: + date = datetime.strptime(key, "%Y-%m-%d") + year = date.year + month = date.month + season = get_season(month) + if season == "rabi": + current_year = year - 1 if month in (1, 2) else year + elif season == "zaid": + current_year = year - 1 if month in (3, 4, 5, 6) else year + else: + current_year = year + data = json.loads(value) + + for json_var, result_var in variable_mapping.items(): + if json_var in data: + if current_year not in results[result_var][season]: + results[result_var][season][current_year] = 0.0 + results[result_var][season][current_year] += float( + data[json_var] + ) + + except (ValueError, json.JSONDecodeError) as e: + print(f"Error processing data for date {key}: {e}") + continue + return results + + +def create_excel_seas_mws(processed_data, output_file, writer, start_year, end_year): + variables = ["precipitation", "et", "runoff", "delta g", "g"] + seasons = ["kharif", "rabi", "zaid"] + + data = {"UID": []} + for variable in variables: + for year in range(start_year, end_year + 1): + for season in seasons: + end_to_year = year + 1 + column_name = f"{variable}_{season}_in_mm_{year}-{end_to_year}" + data[column_name] = [] + + for feature_data in processed_data: + data["UID"].append(feature_data["UID"]) + for variable in variables: + for year in range(start_year, end_year + 1): + for season in seasons: + end_to_year = year + 1 + column_name = f"{variable}_{season}_in_mm_{year}-{end_to_year}" + value = feature_data[variable].get(season, {}).get(year, 0.0) + data[column_name].append(value) + + df = pd.DataFrame(data) + df = df.sort_values("UID") + + ## for roundoff all numeric value upto 2 decimal + numeric_cols = df.select_dtypes(include=["int64", "float64"]).columns + df[numeric_cols] = df[numeric_cols].round(2) + + df.to_excel(writer, sheet_name="hydrological_seasonal", index=False) + print(f"Excel file created hydrological_seasonal") + + +def create_excel_for_village_boun(old_geojson, writer): + results = [] + + village_data = {} + + for feature in old_geojson["features"]: + properties = feature["properties"] + + # Extract properties + state_census_ID = properties.get("state_cen", None) + dist_census_ID = properties.get("dist_cen", None) + block_census_ID = properties.get("block_cen", None) + village_id = properties.get("vill_ID", None) + village_name = properties.get("vill_name", None) + + # Initialize village data using village_id as the key + if village_id not in village_data: + village_data[village_id] = { + "village_name": village_name, + "TOT_P": 0, + "P_LIT": 0, + "P_SC": 0, + "P_ST": 0, + "state_census_ID": state_census_ID, + "dist_census_ID": dist_census_ID, + "block_census_ID": block_census_ID, + "geometry": feature["geometry"], + } + + village_data[village_id]["TOT_P"] += properties.get("TOT_P", 0) + village_data[village_id]["P_LIT"] += properties.get("P_LIT", 0) + village_data[village_id]["P_SC"] += properties.get("P_SC", 0) + village_data[village_id]["P_ST"] += properties.get("P_ST", 0) + + for village_id, data in village_data.items(): + total_popu = data["TOT_P"] + literacy_rate = data["P_LIT"] * 100 / total_popu if total_popu > 0 else 0.0 + total_SC_popu = data["P_SC"] + total_ST_popu = data["P_ST"] + sc_perce = (data["P_SC"] * 100 / total_popu) if total_popu > 0 else 0.0 + st_perce = (data["P_ST"] * 100 / total_popu) if total_popu > 0 else 0.0 + + results.append( + { + "state_census_ID": data["state_census_ID"], + "dist_census_ID": data["dist_census_ID"], + "block_census_ID": data["block_census_ID"], + "village_id": village_id, + "village_name": data["village_name"], + "total_population_count": total_popu, + "total_SC_population_count": total_SC_popu, + "total_ST_population_count": total_ST_popu, + "literacy_rate_percent": literacy_rate, + "SC_percent": sc_perce, + "ST_percent": st_perce, + } + ) + + results_df = pd.DataFrame(results) + results_df.to_excel(writer, sheet_name="social_economic_indicator", index=False) + + print(f"Excel file created for social_economic_indicator") + + +def download_layers_excel_file(state, district, block): + base_path = os.path.join(EXCEL_PATH, "data/stats_excel_files") + state_path = os.path.join(base_path, state.upper()) + district_path = os.path.join(state_path, district.upper()) + filename = f"{district}_{block}.xlsx" + file_path = os.path.join(district_path, filename) + + output_dir = Path(file_path).parent + output_dir.mkdir(parents=True, exist_ok=True) + + if not os.path.exists(file_path): + try: + if not get_vector_layer_geoserver(state, district, block): + return Response( + { + "status": "error", + "message": "Failed to generate vector layer from GeoServer.", + }, + status=status.HTTP_500_INTERNAL_SERVER_ERROR, + ) + if not os.path.exists(file_path): + return Response( + {"status": "error", "message": "Failed to generate Excel file."}, + status=status.HTTP_500_INTERNAL_SERVER_ERROR, + ) + + except Exception as e: + return Response( + { + "status": "error", + "message": f"Error during file generation: {str(e)}", + }, + status=status.HTTP_500_INTERNAL_SERVER_ERROR, + ) + else: + print(f"Excel file already exists at: {file_path}") + + # Single file reading logic - only written once! + if os.path.exists(file_path): + try: + with open(file_path, "rb") as file: + response = HttpResponse( + file.read(), + content_type="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", + ) + response["Content-Disposition"] = f"attachment; filename={filename}" + return response + except Exception as e: + return Response( + {"status": "error", "message": f"Error reading file: {str(e)}"}, + status=status.HTTP_500_INTERNAL_SERVER_ERROR, + ) + else: + return Response( + {"status": "error", "message": "Failed to locate Excel file."}, + status=status.HTTP_404_NOT_FOUND, + ) + + +def generate_stats_excel_file(state, district, block): + """ + Deletes existing Excel layer file and forces regeneration. + Then returns the newly generated file. + """ + base_path = os.path.join(EXCEL_PATH, "data/stats_excel_files") + state_path = os.path.join(base_path, state.upper()) + district_path = os.path.join(state_path, district.upper()) + filename = f"{district}_{block}.xlsx" + file_path = os.path.join(district_path, filename) + + try: + # Create directory if it doesn't exist + output_dir = Path(file_path).parent + output_dir.mkdir(parents=True, exist_ok=True) + + # ALWAYS delete existing file if it exists + if os.path.exists(file_path): + os.remove(file_path) + + from .mws_indicators import generate_mws_data_for_kyl_filters + from .village_indicators import get_generate_filter_data_village + from public_api.views import get_tehsil_json + + get_vector_layer_geoserver(state, district, block) + get_tehsil_json(state, district, block, 1) + generate_mws_data_for_kyl_filters(state, district, block, "json", 1) + get_generate_filter_data_village(state, district, block, 1) + + if not os.path.exists(file_path): + return Response( + { + "status": "error", + "message": "Excel file generation completed but file not found.", + }, + status=status.HTTP_500_INTERNAL_SERVER_ERROR, + ) + + # Return the newly generated file + with open(file_path, "rb") as file: + response = HttpResponse( + file.read(), + content_type="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", + ) + response["Content-Disposition"] = f"attachment; filename={filename}" + return response + + except Exception as e: + return Response( + {"status": "error", "message": f"Failed to generate stats excel: {str(e)}"}, + status=status.HTTP_500_INTERNAL_SERVER_ERROR, + ) + + +def add_sheets_to_excel(state, district, block, sheets): + try: + sheets_to_add = [sheet.strip() for sheet in sheets.split(",") if sheet.strip()] + results = [] + + for sheet in sheets_to_add: + r = get_vector_layer_geoserver(state, district, block, sheet) + results.append(r) + + from .mws_indicators import generate_mws_data_for_kyl_filters + from .village_indicators import get_generate_filter_data_village + + from public_api.views import get_tehsil_json + + get_tehsil_json(state, district, block, 1) + generate_mws_data_for_kyl_filters(state, district, block, "json", 1) + get_generate_filter_data_village(state, district, block, 1) + + successful = [r for r in results if r["status"] == "success"] + failed = [r for r in results if r["status"] == "failed"] + + response_data = { + "status": "success" if successful else "failed", + "message": f"Stats data added info. {len(successful)} successful, {len(failed)} failed.", + } + + return response_data + + except Exception as e: + return { + "status": "error", + "message": str(e), + } diff --git a/stats_generator/village_indicators.py b/stats_generator/village_indicators.py index b3bbdee7..e33942c2 100644 --- a/stats_generator/village_indicators.py +++ b/stats_generator/village_indicators.py @@ -51,66 +51,66 @@ def safe_val(v): result = { "essential_education_infra": get_max( [ - row.get("school_primary_distance", -1), - row.get("school_upper_primary_distance", -1), - row.get("school_secondary_distance", -1), + row.get("school_primary_distance_in_km", -1), + row.get("school_upper_primary_distance_in_km", -1), + row.get("school_secondary_distance_in_km", -1), ] ), "higher_education_infra": get_min( [ - row.get("school_higher_secondary_distance", -1), - row.get("college_distance", -1), - row.get("universities_distance", -1), + row.get("school_higher_secondary_distance_in_km", -1), + row.get("college_distance_in_km", -1), + row.get("universities_distance_in_km", -1), ] ), "essential_health_services": get_max( [ - row.get("health_sub_cen_distance", -1), - row.get("health_phc_distance", -1), + row.get("health_sub_cen_distance_in_km", -1), + row.get("health_phc_distance_in_km", -1), ] ), "advanced_health_services": get_min( [ - row.get("health_chc_distance", -1), - row.get("health_dis_h_distance", -1), - row.get("health_s_t_h_distance", -1), + row.get("health_chc_distance_in_km", -1), + row.get("health_dis_h_distance_in_km", -1), + row.get("health_s_t_h_distance_in_km", -1), ] ), "public_distribution_system": get_max( [ - row.get("pds_distance", -1), + row.get("pds_distance_in_km", -1), ] ), "financial_inclusion": get_max( [ - row.get("csc_distance", -1), - row.get("bank_mitra_distance", -1), - row.get("bank_branch_distance", -1), - row.get("bank_atm_distance", -1), + row.get("csc_distance_in_km", -1), + row.get("bank_mitra_distance_in_km", -1), + row.get("bank_branch_distance_in_km", -1), + row.get("bank_atm_distance_in_km", -1), ] ), "agri_market_access": get_min( [ - row.get("apmc_distance", -1), - row.get("agri_industry_markets_trading_distance", -1), + row.get("apmc_distance_in_km", -1), + row.get("agri_industry_markets_trading_distance_in_km", -1), ] ), "post_harvest_infra": get_min( [ - row.get("agri_industry_storage_warehousing_distance", -1), - row.get("agri_industry_distribution_utilities_distance", -1), - row.get("agri_industry_agri_processing_distance", -1), - row.get("agri_industry_industrial_manufacturing_distance", -1), + row.get("agri_industry_storage_warehousing_distance_in_km", -1), + row.get("agri_industry_distribution_utilities_distance_in_km", -1), + row.get("agri_industry_agri_processing_distance_in_km", -1), + row.get("agri_industry_industrial_manufacturing_distance_in_km", -1), ] ), "farmer_cooperatives_access": safe_val( - row.get("agri_industry_co_operatives_societies_distance", -1) + row.get("agri_industry_co_operatives_societies_distance_in_km", -1) ), "livestock_management_centers": safe_val( - row.get("agri_industry_dairy_animal_husbandry_distance", -1) + row.get("agri_industry_dairy_animal_husbandry_distance_in_km", -1) ), "agricultural_support_infrastructure": safe_val( - row.get("agri_industry_agri_support_infrastructure_distance", -1) + row.get("agri_industry_agri_support_infrastructure_distance_in_km", -1) ), } diff --git a/templates/dpr/base.html b/templates/dpr/base.html new file mode 100644 index 00000000..d1eb68e2 --- /dev/null +++ b/templates/dpr/base.html @@ -0,0 +1,147 @@ + + + + + + + + + + +

+ {{ t.common.dpr_title }} +

+ +
+ {{ current_date }} +
+ + {% include "dpr/section_a.html" %} + {% include "dpr/section_b.html" %} + {% include "dpr/section_c_socio_economic.html" %} + {% include "dpr/section_c_mgnrega.html" %} + {% include "dpr/section_c_crop.html" %} + {% include "dpr/section_c_livelihood.html" %} + {% include "dpr/section_d_base.html" %} + {% include "dpr/section_d_mws_information.html" %} + {% include "dpr/section_d_well_summary.html" %} + {% include "dpr/section_d_detail_well_info.html" %} + {% include "dpr/section_d_water_structure_summary.html" %} + {% include "dpr/section_d_detail_water_structure.html" %} + {% include "dpr/section_e_base.html" %} + {% include "dpr/section_e_maintenance_work_by_asset_type.html" %} + {% include "dpr/section_e_recharge_structure.html" %} + {% include "dpr/section_e_irrigation_structure.html" %} + {% include "dpr/section_e_water_structure.html" %} + {% include "dpr/section_e_rs_swb.html" %} + {% include "dpr/section_f.html" %} + {% include "dpr/section_g.html" %} + +
+
+

{{ footnote }}

+
+ + + + \ No newline at end of file diff --git a/templates/dpr/section_a.html b/templates/dpr/section_a.html new file mode 100644 index 00000000..7b6a804c --- /dev/null +++ b/templates/dpr/section_a.html @@ -0,0 +1,61 @@ +
+ +

+ {{ t.section_a.title }} +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ {{ t.section_a.organization }} + + {{ section_a.organization.name|default:t.common.na }} +
+ {{ t.section_a.project }} + + {{ section_a.project.name|default:t.common.na }} +
+ {{ t.section_a.plan }} + + {{ section_a.plan|default:t.common.na }} +
+ {{ t.section_a.facilitator }} + + {{ section_a.facilitator_name|default:t.common.na }} +
+ {{ t.section_a.process_preparation }} + + {{ t.section_a.process_preparation_value }} +
+ +
\ No newline at end of file diff --git a/templates/dpr/section_b.html b/templates/dpr/section_b.html new file mode 100644 index 00000000..0be2ac45 --- /dev/null +++ b/templates/dpr/section_b.html @@ -0,0 +1,117 @@ +
+ +

+ {{ t.section_b.title }} +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ {{ t.section_b.village }} + + {{ section_b.village_name|default:t.common.na }} +
+ {{ t.section_b.gram_panchayat }} + + {{ section_b.gram_panchayat|default:t.common.na }} +
+ {{ t.section_b.tehsil }} + + {{ section_b.tehsil|default:t.common.na }} +
+ {{ t.section_b.district }} + + {{ section_b.district|default:t.common.na }} +
+ {{ t.section_b.state }} + + {{ section_b.state|default:t.common.na }} +
+ {{ t.section_b.number_of_settlements }} + + {{ section_b.total_settlements|default:t.common.na }} +
+ {{ t.section_b.intersecting_mws }} + + + {% if section_b.settlement_mws_pairs %} + + + + + + + + {% for item in section_b.settlement_mws_pairs %} + + + + + {% endfor %} + +
+ {{ t.section_b.intersecting_mws_inner_settlement }} + + {{ t.section_b.intersecting_mws_inner_mws }} +
{{ item.settlement }}{{ item.mws_id }}
+ + {% else %} + No intersecting watersheds + {% endif %} + +
+ {{ t.section_b.latitude_and_longitude }} + + {{ section_b.village_coordinates|default:t.common.na }} +
+ +
\ No newline at end of file diff --git a/templates/dpr/section_c_crop.html b/templates/dpr/section_c_crop.html new file mode 100644 index 00000000..84e00c09 --- /dev/null +++ b/templates/dpr/section_c_crop.html @@ -0,0 +1,72 @@ +
+ +

+ {{ t.section_c_crop.title }} +

+ + + + + + + + + + + + + + + + + + {% for item in section_c.crop_info %} + + + + + + + + + + + + + + + + + + + + + + + + {% endfor %} +
{{ t.section_c_crop.settlement_name }}{{ t.section_c_crop.irrigation_source }}{{ t.section_c_crop.crop_in_kharif }}{{ t.section_c_crop.kharif_acerage }}{{ t.section_c_crop.crop_in_rabi }}{{ t.section_c_crop.rabi_acerage }}{{ t.section_c_crop.crop_in_zaid }}{{ t.section_c_crop.zaid_acerage }}{{ t.section_c_crop.crop_intensity }}{{ t.section_c_crop.land_classification }}
+ {{ item.beneficiary_settlement|default:t.common.na }} + + {{ item.irrigation_source|default:t.common.na }} + + + {{ item.kharif_crops|default:t.common.na }} + + + {{ item.kharif_acres |default:t.common.na }} + + {{ item.rabi_crops |default:t.common.na}} + + {{ item.rabi_acres |default:t.common.na }} + + {{ item.zaid_crops |default:t.common.na }} + + {{ item.zaid_acres |default:t.common.na }} + + {{ item.cropping_intensity |default:t.common.na }} + + {{ item.land_classification |default:t.common.na }} +
+ +
\ No newline at end of file diff --git a/templates/dpr/section_c_livelihood.html b/templates/dpr/section_c_livelihood.html new file mode 100644 index 00000000..8bab15de --- /dev/null +++ b/templates/dpr/section_c_livelihood.html @@ -0,0 +1,52 @@ + +
+ +

+ {{ t.section_c_livelihood.title }} +

+ + + + + + + + + + + + + {% for item in section_c.livestock_info %} + + + + + + + + + + + + + + + + {% endfor %} +
{{ t.section_c_livelihood.settlement_name }}{{ t.section_c_livelihood.goat }}{{ t.section_c_livelihood.sheep }}{{ t.section_c_livelihood.cattle }}{{ t.section_c_livelihood.piggery }}{{ t.section_c_livelihood.poultary }}
+ {{ item.settlement_name|default:t.common.na }} + + {{ item.goats|default:t.common.na }} + + + {{ item.sheep|default:t.common.na }} + + + {{ item.cattle |default:t.common.na }} + + {{ item.piggery |default:t.common.na}} + + {{ item.poultry |default:t.common.na }} +
+ +
\ No newline at end of file diff --git a/templates/dpr/section_c_mgnrega.html b/templates/dpr/section_c_mgnrega.html new file mode 100644 index 00000000..040bd3e6 --- /dev/null +++ b/templates/dpr/section_c_mgnrega.html @@ -0,0 +1,66 @@ +
+ +

+ {{ t.section_c_mgnrega.title }} +

+ + + + + + + + + + + + + {% for item in section_c.mgnrega %} + + + + + + + + + + + + + + + + {% endfor %} +
{{ t.section_c_mgnrega.settlement_name }}{{ t.section_c_mgnrega.total_household_applied_job_card }}{{ t.section_c_mgnrega.work_days_previous_year }}{{ t.section_c_mgnrega.demand_made_in_previous_year }}{{ t.section_c_mgnrega.involved_in_village_planing }}{{ t.section_c_mgnrega.issues }}
+ {{ item.settlement_name|default:t.common.na }} + +

{{ t.section_c_mgnrega.applied }}: + {% if item.nrega_job_applied == 0 %} + {{ t.common.na }} + {% else %} + {{ item.nrega_job_applied }} + {% endif %} +

+

{{ t.section_c_mgnrega.having }}: + {% if item.nrega_job_card == 0 %} + {{ t.common.na }} + {% else %} + {{ item.nrega_job_card }} + {% endif %} +

+
+ {% if item.nrega_work_days == 0 %} + {{ t.common.na }} + {% else %} + {{ item.nrega_work_days }} + {% endif %} + + {{ item.nrega_past_work_label|default:t.common.na }} + + {{ item.nrega_demand_label|default:t.common.na}} + + {{ item.nrega_issues_label|default:t.common.na }} +
+ +
\ No newline at end of file diff --git a/templates/dpr/section_c_socio_economic.html b/templates/dpr/section_c_socio_economic.html new file mode 100644 index 00000000..341a8a77 --- /dev/null +++ b/templates/dpr/section_c_socio_economic.html @@ -0,0 +1,82 @@ +
+ +

+ {{ t.section_c_socio_economic.title }} +

+ +

{{ t.section_c_socio_economic.description }}

+ + + + + + + + + + + + + + + + {% for item in section_c.socio_eco %} + + + + + + + + + + + + + + + + {% endfor %} + + +
{{ t.section_c_socio_economic.name_of_the_settlement }}{{ t.section_c_socio_economic.number_of_households }}{{ t.section_c_socio_economic.settlement_type }}{{ t.section_c_socio_economic.caste_group }}{{ t.section_c_socio_economic.total_households }}{{ t.section_c_socio_economic.marginal_farmer }}
+ {{ item.settlement_name|default:t.common.na }} + + {{ item.number_of_households|default:t.common.na }} + + {{ item.largest_caste_label|default:t.common.na }} + + {% if item.largest_caste|lower == "single caste group" %} + {{ item.smallest_caste_label|default:t.common.na }} + {% elif item.largest_caste|lower == "mixed caste group" %} + {{ item.settlement_status_label|default:t.common.na }} + {% else %} + {{ t.common.na }} + {% endif %} + + + + + + + + + + + + + + + + + + + + + +
{{ t.section_c_socio_economic.sc }}{{ item.data_settlement.count_sc|default:t.common.na }}
{{ t.section_c_socio_economic.st }}{{ item.data_settlement.count_st|default:t.common.na }}
{{ t.section_c_socio_economic.obc }}{{ item.data_settlement.count_obc|default:t.common.na }}
{{ t.section_c_socio_economic.gen }}{{ item.data_settlement.count_general|default:t.common.na }}
+
+ {{ item.farmer_family.marginal_farmers|default:t.common.na }} +
+ +
\ No newline at end of file diff --git a/templates/dpr/section_d_base.html b/templates/dpr/section_d_base.html new file mode 100644 index 00000000..64ef8085 --- /dev/null +++ b/templates/dpr/section_d_base.html @@ -0,0 +1,7 @@ +
+ +

{{ t.section_d_base.title }}

+ +

{{ t.section_d_base.description }}

+ +
\ No newline at end of file diff --git a/templates/dpr/section_d_detail_water_structure.html b/templates/dpr/section_d_detail_water_structure.html new file mode 100644 index 00000000..db7b7df7 --- /dev/null +++ b/templates/dpr/section_d_detail_water_structure.html @@ -0,0 +1,87 @@ +
+ +

{{ t.section_d_detail_water_structure.title }}

+ + {% for waterbody in section_d.water_structures %} + +

+ {{ waterbody.settlement|default:t.common.na }} +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
{{ t.section_d_detail_water_structure.mws_id }}{{ waterbody.mws_id|default:t.common.na }}
{{ t.section_d_detail_water_structure.beneficiary_settlement }}{{ waterbody.settlement|default:t.common.na }}
{{ t.section_d_detail_water_structure.water_structure_owner }}{{ waterbody.owner|default:t.common.na }}
{{ t.section_d_detail_water_structure.beneficiary_name }}{{ waterbody.beneficiary_name|default:t.common.na }}
{{ t.section_d_detail_water_structure.beneficiary_father_name }}{{ waterbody.father_name|default:t.common.na }}
{{ t.section_d_detail_water_structure.who_manage }}{{ waterbody.who_manages|default:t.common.na }}
{{ t.section_d_detail_water_structure.caste_use }}{{ waterbody.caste_uses|default:t.common.na }}
{{ t.section_d_detail_water_structure.benefitted_household }}{{ waterbody.households_benefitted|default:t.common.na }}
{{ t.section_d_detail_water_structure.water_structure_type }}{{ waterbody.structure_type|default:t.common.na }}
{{ t.section_d_detail_water_structure.usage }}{{ waterbody.usage|default:t.common.na }}
{{ t.section_d_detail_water_structure.maintenance }}{{ waterbody.need_maintenance|default:t.common.na }}
{{ t.section_d_detail_water_structure.repair_activities }}{{ waterbody.repair_activities|default:t.common.na }}
{{ t.section_d_detail_water_structure.latitude }}{{ waterbody.latitude|default:t.common.na }}
{{ t.section_d_detail_water_structure.longitude }}{{ waterbody.longitude|default:t.common.na }}
+ + {% endfor %} + +
\ No newline at end of file diff --git a/templates/dpr/section_d_detail_well_info.html b/templates/dpr/section_d_detail_well_info.html new file mode 100644 index 00000000..378d25a4 --- /dev/null +++ b/templates/dpr/section_d_detail_well_info.html @@ -0,0 +1,87 @@ +
+ +

{{ t.section_d_detail_well.title }}

+ + {% for well in section_d.wells %} + +

+ {{ well.settlement|default:t.common.na }} +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
{{ t.section_d_detail_well.mws_id }}{{ well.mws_id|default:t.common.na }}
{{ t.section_d_detail_well.beneficiary_settlement }}{{ well.settlement|default:t.common.na }}
{{ t.section_d_detail_well.well_type }}{{ well.well_type|default:t.common.na }}
{{ t.section_d_detail_well.well_owner }}{{ well.owner|default:t.common.na }}
{{ t.section_d_detail_well.beneficiary_name }}{{ well.beneficiary_name|default:t.common.na }}
{{ t.section_d_detail_well.beneficiary_father_name }}{{ well.father_name|default:t.common.na }}
{{ t.section_d_detail_well.water_availability }}{{ well.water_availability|default:t.common.na }}
{{ t.section_d_detail_well.households_benefitted }}{{ well.households_benefitted|default:t.common.na }}
{{ t.section_d_detail_well.caste_use }}{{ well.caste_uses|default:t.common.na }}
{{ t.section_d_detail_well.well_usage }}{{ well.well_usage|default:t.common.na }}
{{ t.section_d_detail_well.need_maintenance }}{{ well.need_maintenance|default:t.common.na }}
{{ t.section_d_detail_well.repair_activities }}{{ well.repair_activities|default:t.common.na }}
{{ t.section_d_detail_well.latitude }}{{ well.latitude|default:t.common.na }}
{{ t.section_d_detail_well.longitude }}{{ well.longitude|default:t.common.na }}
+ + {% endfor %} + +
\ No newline at end of file diff --git a/templates/dpr/section_d_mws_information.html b/templates/dpr/section_d_mws_information.html new file mode 100644 index 00000000..24f14d7a --- /dev/null +++ b/templates/dpr/section_d_mws_information.html @@ -0,0 +1,31 @@ +
+ + + + + + + + + + + + {% for item in section_d.mws %} + + + + + + + + {% endfor %} + + + +
{{ t.section_d_mws_info.mws_id }}{{ t.section_d_mws_info.lat_lon }}
+ {{ item.mws_id|default:t.common.na }} + + {{ item.centroid|default:t.common.na }} +
+ +
\ No newline at end of file diff --git a/templates/dpr/section_d_water_structure_summary.html b/templates/dpr/section_d_water_structure_summary.html new file mode 100644 index 00000000..f98e2798 --- /dev/null +++ b/templates/dpr/section_d_water_structure_summary.html @@ -0,0 +1,36 @@ +
+ +

{{ t.section_d_water_structure_summary.title }}

+ + + + + + + + + + + + + + + {% for item in section_d.water_summary %} + + + + + + + + + + + + {% endfor %} + + + +
{{ t.section_d_water_structure_summary.beneficiary_settlement }}{{ t.section_d_water_structure_summary.water_structure_type }}{{ t.section_d_water_structure_summary.waterbodies_number }}{{ t.section_d_water_structure_summary.household_benefitted }}
{{ item.settlement|default:t.common.na }}{{ item.structure_type|default:t.common.na }}{{ item.count|default:t.common.na }}{{ item.households|default:t.common.na }}
+ +
\ No newline at end of file diff --git a/templates/dpr/section_d_well_summary.html b/templates/dpr/section_d_well_summary.html new file mode 100644 index 00000000..907ed544 --- /dev/null +++ b/templates/dpr/section_d_well_summary.html @@ -0,0 +1,39 @@ +
+ +

{{ t.section_d_well_summary.title }}

+ + + + + + + + + + + + + + {% for item in section_d.well_summary %} + + + + + + + + + + {% endfor %} + + + +
{{ t.section_d_well_summary.beneficiary_settlement }}{{ t.section_d_well_summary.no_of_wells }}{{ t.section_d_well_summary.household_benefitted }}
+ {{ item.settlement|default:t.common.na }} + + {{ item.num_wells|default:t.common.na }} + + {{ item.households|default:t.common.na }} +
+ +
\ No newline at end of file diff --git a/templates/dpr/section_e_base.html b/templates/dpr/section_e_base.html new file mode 100644 index 00000000..8af3e81f --- /dev/null +++ b/templates/dpr/section_e_base.html @@ -0,0 +1,7 @@ +
+ +

{{ t.section_e.title }}

+ +

{{ t.section_e.description }}

+ +
\ No newline at end of file diff --git a/templates/dpr/section_e_irrigation_structure.html b/templates/dpr/section_e_irrigation_structure.html new file mode 100644 index 00000000..ce811c3f --- /dev/null +++ b/templates/dpr/section_e_irrigation_structure.html @@ -0,0 +1,48 @@ +

{{ t.section_e.irrigation_structures }}

+ + + + + + + + + + + + + + + + + + + {% for item in section_e.agri %} + + + + + + + + + + + + + + + + + + + + {% empty %} + + + + {% endfor %} + + + +
{{ t.section_e.type_of_demand }}{{ t.section_e.beneficiary_settlement }}{{ t.section_e.beneficiary_name }}{{ t.section_e.father_name }}{{ t.section_e.irrigation_structure }}{{ t.section_e.repair_activities }}{{ t.section_e.latitude }}{{ t.section_e.longitude }}
{{ item.demand_type|default:t.common.na }}{{ item.beneficiary_settlement|default:t.common.na }}{{ item.beneficiary_name|default:t.common.na }}{{ item.beneficiary_father_name|default:t.common.na }}{{ item.structure_type|default:t.common.na }}{{ item.repair_activities }}{{ item.latitude }}{{ item.longitude }}
{{ t.common.na }}
\ No newline at end of file diff --git a/templates/dpr/section_e_maintenance_work_by_asset_type.html b/templates/dpr/section_e_maintenance_work_by_asset_type.html new file mode 100644 index 00000000..ec7e649f --- /dev/null +++ b/templates/dpr/section_e_maintenance_work_by_asset_type.html @@ -0,0 +1,26 @@ +

{{ t.section_e.asset_type_heading }}

+ + + + + + + + + + + + + + + + + + + + + + + + +
{{ t.section_e.asset_type }}
{{ t.section_e.recharge_structure_asset }}
{{ t.section_e.irrigation_structure_asset }}
{{ t.section_e.water_structure_asset }}
{{ t.section_e.rs_water_structure_asset }}
\ No newline at end of file diff --git a/templates/dpr/section_e_recharge_structure.html b/templates/dpr/section_e_recharge_structure.html new file mode 100644 index 00000000..0fcb52c4 --- /dev/null +++ b/templates/dpr/section_e_recharge_structure.html @@ -0,0 +1,48 @@ +

{{ t.section_e.water_recharge_structures }}

+ + + + + + + + + + + + + + + + + + + {% for item in section_e.gw %} + + + + + + + + + + + + + + + + + + + + {% empty %} + + + + {% endfor %} + + + +
{{ t.section_e.type_of_demand }}{{ t.section_e.beneficiary_settlement }}{{ t.section_e.beneficiary_name }}{{ t.section_e.father_name }}{{ t.section_e.recharge_structure }}{{ t.section_e.repair_activities }}{{ t.section_e.latitude }}{{ t.section_e.longitude }}
{{ item.demand_type|default:t.common.na }}{{ item.beneficiary_settlement|default:t.common.na }}{{ item.beneficiary_name|default:t.common.na }}{{ item.beneficiary_father_name|default:t.common.na }}{{ item.structure_type|default:t.common.na }}{{ item.repair_activities|default:t.common.na}}{{ item.latitude }}{{ item.longitude }}
{{ t.common.na }}
\ No newline at end of file diff --git a/templates/dpr/section_e_rs_swb.html b/templates/dpr/section_e_rs_swb.html new file mode 100644 index 00000000..fb77f219 --- /dev/null +++ b/templates/dpr/section_e_rs_swb.html @@ -0,0 +1,48 @@ +

{{ t.section_e.remote_sensed_surface_water_structures }}

+ + + + + + + + + + + + + + + + + + + {% for item in section_e.swb_rs %} + + + + + + + + + + + + + + + + + + + + {% empty %} + + + + {% endfor %} + + + +
{{ t.section_e.type_of_demand }}{{ t.section_e.beneficiary_settlement }}{{ t.section_e.beneficiary_name }}{{ t.section_e.father_name }}{{ t.section_e.type_of_work }}{{ t.section_e.repair_activities }}{{ t.section_e.latitude }}{{ t.section_e.longitude }}
{{ item.demand_type|default:t.common.na }}{{ item.beneficiary_settlement|default:t.common.na }}{{ item.beneficiary_name|default:t.common.na }}{{ item.beneficiary_father_name|default:t.common.na }}{{ item.structure_type|default:t.common.na }}{{ item.repair_activities | default:t.common.na}}{{ item.latitude }}{{ item.longitude }}
{{ t.common.na }}
\ No newline at end of file diff --git a/templates/dpr/section_e_water_structure.html b/templates/dpr/section_e_water_structure.html new file mode 100644 index 00000000..7f8d994b --- /dev/null +++ b/templates/dpr/section_e_water_structure.html @@ -0,0 +1,48 @@ +

{{ t.section_e.surface_water_structures }}

+ + + + + + + + + + + + + + + + + + + {% for item in section_e.swb %} + + + + + + + + + + + + + + + + + + + + {% empty %} + + + + {% endfor %} + + + +
{{ t.section_e.type_of_demand }}{{ t.section_e.beneficiary_settlement }}{{ t.section_e.beneficiary_name }}{{ t.section_e.father_name }}{{ t.section_e.type_of_work }}{{ t.section_e.repair_activities }}{{ t.section_e.latitude }}{{ t.section_e.longitude }}
{{ item.demand_type|default:t.common.na }}{{ item.beneficiary_settlement|default:t.common.na }}{{ item.beneficiary_name|default:t.common.na }}{{ item.beneficiary_father_name|default:t.common.na }}{{ item.structure_type|default:t.common.na }}{{ item.repair_activities }}{{ item.latitude }}{{ item.longitude }}
{{ t.common.na }}
\ No newline at end of file diff --git a/templates/dpr/section_f.html b/templates/dpr/section_f.html new file mode 100644 index 00000000..3305f872 --- /dev/null +++ b/templates/dpr/section_f.html @@ -0,0 +1,64 @@ +
+ +

+ {{ t.section_f.title }} +

+ +

+ {{ t.section_f.description }} +

+ + + + + + + + + + + + + + + + + + + + + {% for item in section_f.works %} + + + + + + + + + + + + + + + + + + + + + + + + {% empty %} + + + + {% endfor %} + + + +
{{ t.section_f.serial_no }}{{ t.section_f.work_category }}{{ t.section_f.type_of_demand }}{{ t.section_f.work_demand }}{{ t.section_f.beneficiary_settlement }}{{ t.section_f.beneficiary_name }}{{ t.section_f.gender }}{{ t.section_f.father_name }}{{ t.section_f.latitude }}{{ t.section_f.longitude }}
{{ forloop.counter }}{{ item.work_category|default:t.common.na }}{{ item.demand_type|default:t.common.na }}{{ item.work_demand|default:t.common.na }}{{ item.beneficiary_settlement|default:t.common.na }}{{ item.beneficiary_name|default:t.common.na }}{{ item.gender|default:t.common.na }}{{ item.beneficiary_father_name|default:t.common.na }}{{ item.latitude|default:t.common.na }}{{ item.longitude|default:t.common.na }}
{{ t.common.na }}
+ +
\ No newline at end of file diff --git a/templates/dpr/section_g.html b/templates/dpr/section_g.html new file mode 100644 index 00000000..7895c2d6 --- /dev/null +++ b/templates/dpr/section_g.html @@ -0,0 +1,115 @@ + +
+ +

{{ t.section_g.title }}

+ + +

{{ t.section_g.livestock_and_fisheries }}

+ + + + + + + + + + + + + + + + + + + + {% for item in section_g.livestock_fisheries %} + + + + + + + + + + + + + + + + + + + + + + {% empty %} + + + + {% endfor %} + + + +
{{ t.section_g.livelihood_work }}{{ t.section_g.type_of_demand }}{{ t.section_g.work_demand }}{{ t.section_g.beneficiary_settlement }}{{ t.section_g.beneficiary_name }}{{ t.section_g.gender }}{{ t.section_g.father_name }}{{ t.section_g.latitude }}{{ t.section_g.longitude }}
{{ item.livelihood_work|default:t.common.na }}{{ item.demand_type|default:t.common.na }}{{ item.work_demand|default:t.common.na }}{{ item.beneficiary_settlement|default:t.common.na }}{{ item.beneficiary_name|default:t.common.na }}{{ item.gender|default:t.common.na }}{{ item.beneficiary_father_name|default:t.common.na }}{{ item.latitude|default:t.common.na }}{{ item.longitude|default:t.common.na }}
{{ t.common.na }}
+ + +

{{ t.section_g.plantations_and_kitchen_gardens }}

+ + + + + + + + + + + + + + + + + + + + + {% for item in section_g.plantations %} + + + + + + + + + + + + + + + + + + + + + + + + {% empty %} + + + + {% endfor %} + + + +
{{ t.section_g.livelihood_work }}{{ t.section_g.type_of_demand }}{{ t.section_g.beneficiary_settlement }}{{ t.section_g.beneficiary_name }}{{ t.section_g.gender }}{{ t.section_g.father_name }}{{ t.section_g.plantation_crop }}{{ t.section_g.total_acres }}{{ t.section_g.latitude }}{{ t.section_g.longitude }}
{{ item.livelihood_work|default:t.common.na }}{{ item.demand_type|default:t.common.na }}{{ item.beneficiary_settlement|default:t.common.na }}{{ item.beneficiary_name|default:t.common.na }}{{ item.gender|default:t.common.na }}{{ item.beneficiary_father_name|default:t.common.na }}{{ item.work_demand|default:t.common.na }}{{ item.total_acres|default:t.common.na }}{{ item.latitude|default:t.common.na }}{{ item.longitude|default:t.common.na }}
{{ t.common.na }}
+ +
\ No newline at end of file diff --git a/templates/resource-report.html b/templates/resource-report.html index d5deda26..09bb53e4 100644 --- a/templates/resource-report.html +++ b/templates/resource-report.html @@ -1,1175 +1,1175 @@ - - - - - - - Resource Mapping Report - - - - - - - - - - - - - - - - - - - - -
-
-

Resource and Demand Map Report

-
-
Plan Name: {{plan_name}}
-
Plan ID: {{plan_id}}
-
- -
-
- -
- -
-

Village Overview over LULC

-
-
-
-
-
- Barren Lands -
-
-
- Single Kharif -
-
-
- Single Non-Kharif -
-
-
- Double Cropping -
-
-
- Tripple/Annual/Perennial Cropping -
-
-
- Shrubs and Scrubs -
-
-
- -
- -
-

Settlement Overview for Plan : {{plan_name}}

-
-
- -
- -
- -
-

Well Overview for Plan : {{plan_name}}

-
-
-

Note: Well Distribution on Stream Order Raster

-
-
-
- 1 -
-
-
- 2 -
-
-
- 3 -
-
-
- 4 -
-
-
- 5 -
-
-
- 6 -
-
-
- 7 -
-
-
- 8 -
-
-
- 9 -
-
-
- 10 -
-
-
- 11 -
-
-
- -
- -
-

Water Structures Overview for Plan : {{plan_name}}

-
-
-
-
-
- Good recharge -
-
-
- Moderate recharge -
-
-
- Regeneration -
-
-
- High runoff zone -
-
-
- Surface water harvesting -
-
-
- -
- -
-

Demands Overview for Plan : {{plan_name}}

-
-
-
-
-
- V-shape river valleys, Deep narrow canyons -
-
-
- Lateral midslope incised drainages, Local valleys in plains -
-
-
- Upland incised drainages, Stream headwaters -
-
-
- U-shape valleys -
-
-
- Broad Flat Areas -
-
-
- Broad open slopes -
-
-
- Mesa tops -
-
-
- Upper Slopes -
-
-
- Local ridge/hilltops within broad valleys -
-
-
- Lateral midslope drainage divides, Local ridges in plains -
-
-
- Mountain tops, high ridges -
-
-
- -
-
-

Report generated on | CoRE Stack Team

-

- Please do share your feedback with - contact@core-stack.org. -

-
-
- - - - - + + + + + + + Resource Mapping Report + + + + + + + + + + + + + + + + + + + + +
+
+

Resource and Demand Map Report

+
+
Plan Name: {{plan_name}}
+
Plan ID: {{plan_id}}
+
+ +
+
+ +
+ +
+

Village Overview over LULC

+
+
+
+
+
+ Barren Lands +
+
+
+ Single Kharif +
+
+
+ Single Non-Kharif +
+
+
+ Double Cropping +
+
+
+ Tripple/Annual/Perennial Cropping +
+
+
+ Shrubs and Scrubs +
+
+
+ +
+ +
+

Settlement Overview for Plan : {{plan_name}}

+
+
+ +
+ +
+ +
+

Well Overview for Plan : {{plan_name}}

+
+
+

Note: Well Distribution on Stream Order Raster

+
+
+
+ 1 +
+
+
+ 2 +
+
+
+ 3 +
+
+
+ 4 +
+
+
+ 5 +
+
+
+ 6 +
+
+
+ 7 +
+
+
+ 8 +
+
+
+ 9 +
+
+
+ 10 +
+
+
+ 11 +
+
+
+ +
+ +
+

Water Structures Overview for Plan : {{plan_name}}

+
+
+
+
+
+ Good recharge +
+
+
+ Moderate recharge +
+
+
+ Regeneration +
+
+
+ High runoff zone +
+
+
+ Surface water harvesting +
+
+
+ +
+ +
+

Demands Overview for Plan : {{plan_name}}

+
+
+
+
+
+ V-shape river valleys, Deep narrow canyons +
+
+
+ Lateral midslope incised drainages, Local valleys in plains +
+
+
+ Upland incised drainages, Stream headwaters +
+
+
+ U-shape valleys +
+
+
+ Broad Flat Areas +
+
+
+ Broad open slopes +
+
+
+ Mesa tops +
+
+
+ Upper Slopes +
+
+
+ Local ridge/hilltops within broad valleys +
+
+
+ Lateral midslope drainage divides, Local ridges in plains +
+
+
+ Mountain tops, high ridges +
+
+
+ +
+
+

Report generated on | CoRE Stack Team

+

+ Please do share your feedback with + contact@core-stack.org. +

+
+
+ + + + + \ No newline at end of file diff --git a/users/signals.py b/users/signals.py index 55d4d1d1..34550fc9 100644 --- a/users/signals.py +++ b/users/signals.py @@ -45,7 +45,7 @@ def send_email_to_org_admin(sender, instance, created, **kwargs):

Please take the following action:

  1. - Assign user to a project @@ -57,7 +57,7 @@ def send_email_to_org_admin(sender, instance, created, **kwargs): - © 2025 CoRE Stack. All rights reserved. + © 2025 CoRE Stack. All rights reserved. diff --git a/users/views.py b/users/views.py index 0949e77e..9aa23653 100644 --- a/users/views.py +++ b/users/views.py @@ -1,4 +1,5 @@ import logging +import requests from django.conf import settings from django.contrib.auth.models import Group @@ -96,6 +97,47 @@ def post(self, request, *args, **kwargs): status=status.HTTP_201_CREATED, ) +def verify_recaptcha(token): + try: + response = requests.post( + "https://www.google.com/recaptcha/api/siteverify", + data={ + "secret": settings.RECAPTCHA_SECRET_KEY, + "response": token, + }, + timeout=10, + ) + + response.raise_for_status() + + result = response.json() + success = result.get("success", False) + + if not success: + logger.warning( + "reCAPTCHA verification failed. Response: %s", + result, + ) + + return success + + except requests.exceptions.Timeout: + logger.error("reCAPTCHA request timed out") + return False + + except requests.exceptions.RequestException as exc: + logger.exception( + "Error while verifying reCAPTCHA: %s", + str(exc), + ) + return False + + except Exception as exc: + logger.exception( + "Unexpected error during reCAPTCHA verification: %s", + str(exc), + ) + return False class LoginView(TokenObtainPairView): """ @@ -107,16 +149,55 @@ class LoginView(TokenObtainPairView): def post(self, request, *args, **kwargs): # Call parent class method to validate credentials and get tokens - response = super().post(request, *args, **kwargs) + try: + captcha_token = request.data.get("captcha") + + if not captcha_token: + logger.warning( + "Login attempt without captcha. Username: %s", + request.data.get("username"), + ) + return Response( + {"message": "Captcha is required"}, + status=status.HTTP_400_BAD_REQUEST, + ) + + if not verify_recaptcha(captcha_token): + logger.warning( + "Invalid captcha during login. Username: %s", + request.data.get("username"), + ) + return Response( + {"message": "Invalid captcha"}, + status=status.HTTP_400_BAD_REQUEST, + ) - token = response.data.get("access") - jwt_auth = JWTAuthentication() - validated_token = jwt_auth.get_validated_token(token) - user = jwt_auth.get_user(validated_token) + response = super().post(request, *args, **kwargs) - response.data["user"] = UserSerializer(user).data + token = response.data.get("access") + jwt_auth = JWTAuthentication() + validated_token = jwt_auth.get_validated_token(token) + user = jwt_auth.get_user(validated_token) - return response + logger.info( + "User logged in successfully. User ID: %s Username: %s", + user.id, + user.username, + ) + + response.data["user"] = UserSerializer(user).data + return response + + except Exception as exc: + logger.exception( + "Unexpected error during login: %s", + str(exc), + ) + + return Response( + {"message": "An error occurred during login"}, + status=status.HTTP_500_INTERNAL_SERVER_ERROR, + ) class LogoutView(generics.GenericAPIView): diff --git a/utilities/scripts/tree_health/README.md b/utilities/scripts/tree_health/README.md new file mode 100644 index 00000000..804f110f --- /dev/null +++ b/utilities/scripts/tree_health/README.md @@ -0,0 +1,116 @@ +# Annual Tree Health Layer Generation (Pan-India) + +This guide walks you through the step-by-step process of generating the annual tree health layers for Pan-India. Follow the pipeline carefully to ensure consistency and accuracy across all datasets. + +## Pipeline Overview + +### 1. Download the Data (Colab Script) + +Start by running the Colab download script `IS_DW_sentinel_data_export_new_year.ipynb` to fetch the latest data. This script gathers all the necessary information required to initiate the analysis. + +> **Estimated Runtime:** 3–8 hours to download and export all data to Google Drive, depending on the size of the ACZ. + +> **Note:** Complete this step for all ACZs before moving to the next step. + +--- + +### 2. Predict Results (Colab Script) + +Once the data is ready, use the Colab prediction script `predict_ccd_ch_results.ipynb` to generate prediction outputs. + +The script produces a CSV file containing the predicted values. +m +--- + +### 3. Upload Predictions to GEE (Colab Script) + +Upload the prediction CSV to Google Earth Engine (GEE) using the Colab upload script `uploadAssets.ipynb`. + +--- + +### 4. Convert Feature Collections to Images (GEE Script) + +Run the `fc_to_image.js` script in GEE to convert the uploaded feature collection into an image layer. + +> **Important:** If the target image collection does not already exist (for example, `ccd_2020` or `ch_2020`), create it before proceeding. + +--- + +### 5. Apply Corrections (Colab Script) + +Run the Colab correction script `trees_corrections.ipynb` to make the necessary adjustments to the predictions. + +The script generates a new CSV file containing corrected values. + +> **Important:** This script corrects the data for the current year -1 and current year -2. Therefore, make sure to re-run **Steps 6, 7, and 8** for both years. + +--- + +### 6. Upload Corrected Data to GEE (Colab Script) + +Use the Colab upload corrections script `uploadAssets_correction.ipynb` to upload the correction CSV to GEE. + +--- + +### 7. Convert Corrected Data to Images (GEE Script) + +Run the `fc_to_image_corrections.js` script to convert the corrected data points into image layers. + +> **Important:** Create new image collections if required (for example, `corrections_ccd_2020` or `corrections_ch_2020`). + +> **Note:** Complete this step for all ACZs before moving to the next step. + +--- + +### 8. Run the Modal Change Analysis (GEE Scripts) + +After generating the modal outputs, run the modal change analysis scripts to calculate changes between two years based on the modal values of three consecutive years. + +#### `modal_change_analysis_ccd.js` + +Performs Canopy Cover Density (CCD) change analysis between two years using modal CCD outputs. + +For example, to compute the change between `year1` and `year2`, the script compares: + +- `mode(year1 - 1, year1, year1 + 1)` +- `mode(year2 - 1, year2, year2 + 1)` + +This approach helps reduce year-to-year noise and provides more stable change detection results. + +#### `modal_change_analysis_ch.js` + +Performs Canopy Height (CH) change analysis between two years using modal CH outputs derived from three consecutive years. + +--- + +### 9. Run the Overall Change Analysis (GEE Scripts) + +To generate the overall tree health change between two years, execute the following scripts: + +#### `ccd_change.js` + +Calculates the change in modal Canopy Cover Density (CCD) between the selected years. + +#### `ch_change.js` + +Calculates the change in modal Canopy Height (CH) between the selected years. + +#### `modal_overall_change.js` + +Combines the CCD and CH changes generated by the above scripts to perform the overall tree health change analysis between the selected years. + +--- + +## Workflow Summary + +1. Download data for all ACZs. +2. Generate prediction outputs. +3. Upload predictions to GEE. +4. Convert uploaded feature collections to image layers. +5. Apply corrections to the prediction outputs. +6. Upload corrected data to GEE. +7. Convert corrected feature collections to image layers. +8. Generate modal CCD and CH outputs. +9. Run modal change analysis to calculate CCD and CH changes between years using three-year modal windows. +10. Generate CCD and CH change layers. +11. Run the overall change analysis to combine CCD and CH changes into the final tree health change layer. \ No newline at end of file diff --git a/utilities/scripts/tree_health/colab_notebooks/IS_DW_sentinel_data_export_new_year.ipynb b/utilities/scripts/tree_health/colab_notebooks/IS_DW_sentinel_data_export_new_year.ipynb new file mode 100644 index 00000000..adf409ba --- /dev/null +++ b/utilities/scripts/tree_health/colab_notebooks/IS_DW_sentinel_data_export_new_year.ipynb @@ -0,0 +1 @@ +{"cells":[{"cell_type":"code","execution_count":1,"metadata":{"executionInfo":{"elapsed":11689,"status":"ok","timestamp":1775322096195,"user":{"displayName":"CoRE Stack","userId":"15795073488475210764"},"user_tz":-330},"id":"jteYmnZOnFje"},"outputs":[],"source":["import pandas as pd\n","from glob import glob\n","import re\n","import matplotlib.pyplot as plt\n","import json\n","import numpy as np\n","from scipy import stats as st\n","import ee\n","import shapely.geometry\n","from shapely.geometry import Point, Polygon\n","import geopandas as gpd\n","from math import sqrt\n","from shapely import wkt\n","import os\n","import time\n","import geemap"]},{"cell_type":"code","execution_count":20,"metadata":{"colab":{"base_uri":"https://localhost:8080/","height":17},"executionInfo":{"elapsed":1536,"status":"ok","timestamp":1775323431299,"user":{"displayName":"CoRE Stack","userId":"15795073488475210764"},"user_tz":-330},"id":"5s4VjzimnVwY","outputId":"c4cedd6c-0576-48d5-bfc3-3afc0134bbce"},"outputs":[{"output_type":"display_data","data":{"text/plain":[""],"text/html":["\n"," \n"," "]},"metadata":{}}],"source":["ee.Authenticate()\n","ee.Initialize(project='ext-datasets')"]},{"cell_type":"code","execution_count":4,"metadata":{"colab":{"base_uri":"https://localhost:8080/","height":35},"executionInfo":{"elapsed":44067,"status":"ok","timestamp":1775322195865,"user":{"displayName":"CoRE Stack","userId":"15795073488475210764"},"user_tz":-330},"id":"ESTyH3bjXEMp","outputId":"8d2baa0d-9f83-41d5-e5a5-dee64cdc44bb"},"outputs":[{"output_type":"display_data","data":{"text/plain":[""],"text/html":["\n"," \n"," "]},"metadata":{}},{"output_type":"stream","name":"stdout","text":["Mounted at /content/drive\n"]}],"source":["from google.colab import drive\n","drive.mount('/content/drive')"]},{"cell_type":"code","execution_count":5,"metadata":{"colab":{"base_uri":"https://localhost:8080/","height":17},"executionInfo":{"elapsed":18,"status":"ok","timestamp":1775322209346,"user":{"displayName":"CoRE Stack","userId":"15795073488475210764"},"user_tz":-330},"id":"Ktrj5HIGncZt","outputId":"a9205a41-8e79-4fb5-9080-dd14de322179"},"outputs":[{"output_type":"display_data","data":{"text/plain":[""],"text/html":["\n"," \n"," "]},"metadata":{}}],"source":["year = 2017"]},{"cell_type":"code","execution_count":6,"metadata":{"colab":{"base_uri":"https://localhost:8080/","height":17},"executionInfo":{"elapsed":7,"status":"ok","timestamp":1775322216118,"user":{"displayName":"CoRE Stack","userId":"15795073488475210764"},"user_tz":-330},"id":"VqMB2CcvnfO4","outputId":"a969f5c8-80c1-4854-e1b6-da7d594c8c65"},"outputs":[{"output_type":"display_data","data":{"text/plain":[""],"text/html":["\n"," \n"," "]},"metadata":{}}],"source":["# Choose appropriate ACZ\n","\n","# agroclimatic_zone = \"Eastern Plateau & Hills Region\"\n","agroclimatic_zone = \"Southern Plateau and Hills Region\"\n","# agroclimatic_zone = \"East Coast Plains & Hills Region\"\n","# agroclimatic_zone = \"Western Plateau and Hills Region\"\n","# agroclimatic_zone = \"Central Plateau & Hills Region\"\n","# agroclimatic_zone = \"Lower Gangetic Plain Region\"\n","# agroclimatic_zone = \"Middle Gangetic Plain Region\"\n","# agroclimatic_zone = \"Eastern Himalayan Region\"\n","#agroclimatic_zone = \"Western Himalayan Region\"\n","# agroclimatic_zone = \"Upper Gangetic Plain Region\"\n","# agroclimatic_zone = \"Trans Gangetic Plain Region\"\n","# agroclimatic_zone = \"West Coast Plains & Ghat Region\" ## # Model not available\n","# agroclimatic_zone = \"Gujarat Plains & Hills Region\" ## # Model not available\n","# agroclimatic_zone = \"Western Dry Region\" ## # Model not available"]},{"cell_type":"code","execution_count":7,"metadata":{"colab":{"base_uri":"https://localhost:8080/","height":17},"executionInfo":{"elapsed":4,"status":"ok","timestamp":1775322219096,"user":{"displayName":"CoRE Stack","userId":"15795073488475210764"},"user_tz":-330},"id":"c7iMtt-CniYQ","outputId":"13d24f8f-2ab2-4149-a3a6-a587cb238acb"},"outputs":[{"output_type":"display_data","data":{"text/plain":[""],"text/html":["\n"," \n"," "]},"metadata":{}}],"source":["agroclimaticZone_acronym_dict = {'Eastern Plateau & Hills Region': 'EPAHR',\n"," 'Southern Plateau and Hills Region': 'SPAHR',\n"," 'East Coast Plains & Hills Region': 'ECPHR',\n"," 'Western Plateau and Hills Region': 'WPAHR',\n"," 'Central Plateau & Hills Region': 'CPAHR',\n"," 'Lower Gangetic Plain Region': 'LGPR',\n"," 'Middle Gangetic Plain Region': 'MGPR',\n"," 'Eastern Himalayan Region': 'EHR',\n"," 'Western Himalayan Region': 'WHR',\n"," 'Upper Gangetic Plain Region': 'UGPR',\n"," 'Trans Gangetic Plain Region': 'TGPR',\n"," 'West Coast Plains & Ghat Region': 'WCPGR',\n"," 'Gujarat Plains & Hills Region': 'GPHR',\n"," 'Western Dry Region': 'WDR'}"]},{"cell_type":"code","execution_count":8,"metadata":{"colab":{"base_uri":"https://localhost:8080/","height":17},"executionInfo":{"elapsed":6,"status":"ok","timestamp":1775322221411,"user":{"displayName":"CoRE Stack","userId":"15795073488475210764"},"user_tz":-330},"id":"3npVzImWnlH0","outputId":"d443d000-eda0-4c52-9946-944002d4562e"},"outputs":[{"output_type":"display_data","data":{"text/plain":[""],"text/html":["\n"," \n"," "]},"metadata":{}}],"source":["india_boundary = ee.FeatureCollection(\"projects/ee-mtpictd/assets/harsh/Agroclimatic_regions\")\n","agrozone = india_boundary.filter(ee.Filter.eq('regionname', agroclimatic_zone)).geometry()\n","india_district_boundary = ee.FeatureCollection(\"projects/ee-indiasat/assets/india_district_boundaries\")"]},{"cell_type":"code","execution_count":9,"metadata":{"colab":{"base_uri":"https://localhost:8080/","height":17},"executionInfo":{"elapsed":5,"status":"ok","timestamp":1775322226455,"user":{"displayName":"CoRE Stack","userId":"15795073488475210764"},"user_tz":-330},"id":"-kXRm3DJnnlU","outputId":"fcd76c20-eb44-43d9-e278-824978c6fd92"},"outputs":[{"output_type":"display_data","data":{"text/plain":[""],"text/html":["\n"," \n"," "]},"metadata":{}}],"source":["s1_bands = ['VV', 'VH', 'angle']\n","s2_bands = ['B1','B2', 'B3', 'B4', 'B5', 'B6', 'B7', 'B8', 'B10', 'B11','B12']"]},{"cell_type":"code","execution_count":21,"metadata":{"colab":{"base_uri":"https://localhost:8080/","height":17},"executionInfo":{"elapsed":5,"status":"ok","timestamp":1775323441748,"user":{"displayName":"CoRE Stack","userId":"15795073488475210764"},"user_tz":-330},"id":"HyTMlhh7nqOD","outputId":"9399a761-e6af-4cb1-83ed-b61ee81ae395"},"outputs":[{"output_type":"display_data","data":{"text/plain":[""],"text/html":["\n"," \n"," "]},"metadata":{}}],"source":["START_DATE = {year-2: {'kharif': str(year-2)+'-07-01', 'rabi': str(year-2)+'-11-01', 'zaid': str(year-1)+'-03-01'},\n"," year-1: {'kharif': str(year-1)+'-07-01', 'rabi': str(year-1)+'-11-01', 'zaid': str(year)+'-03-01'},\n"," year: {'kharif': str(year)+'-07-01', 'rabi': str(year)+'-11-01', 'zaid': str(year+1)+'-03-01'}}\n","\n","END_DATE = {year-2: {'kharif': str(year-2)+'-10-31', 'rabi': str(year-1)+'-02-28', 'zaid': str(year-1)+'-06-30'},\n"," year-1: {'kharif': str(year-1)+'-10-31', 'rabi': str(year)+'-02-28', 'zaid': str(year)+'-06-30'},\n"," year: {'kharif': str(year)+'-10-31', 'rabi': str(year+1)+'-02-28', 'zaid': str(year+1)+'-06-30'}}"]},{"cell_type":"code","execution_count":22,"metadata":{"colab":{"base_uri":"https://localhost:8080/","height":73},"executionInfo":{"elapsed":8,"status":"ok","timestamp":1775323443285,"user":{"displayName":"CoRE Stack","userId":"15795073488475210764"},"user_tz":-330},"id":"c8Me4AlontBi","outputId":"918d5170-1440-41f1-8236-578730308d7f"},"outputs":[{"output_type":"display_data","data":{"text/plain":[""],"text/html":["\n"," \n"," "]},"metadata":{}},{"output_type":"stream","name":"stdout","text":["{2015: {'kharif': '2015-07-01', 'rabi': '2015-11-01', 'zaid': '2016-03-01'}, 2016: {'kharif': '2016-07-01', 'rabi': '2016-11-01', 'zaid': '2017-03-01'}, 2017: {'kharif': '2017-07-01', 'rabi': '2017-11-01', 'zaid': '2018-03-01'}}\n","{2015: {'kharif': '2015-10-31', 'rabi': '2016-02-28', 'zaid': '2016-06-30'}, 2016: {'kharif': '2016-10-31', 'rabi': '2017-02-28', 'zaid': '2017-06-30'}, 2017: {'kharif': '2017-10-31', 'rabi': '2018-02-28', 'zaid': '2018-06-30'}}\n"]}],"source":["print(START_DATE)\n","print(END_DATE)"]},{"cell_type":"code","execution_count":12,"metadata":{"colab":{"base_uri":"https://localhost:8080/","height":17},"executionInfo":{"elapsed":10,"status":"ok","timestamp":1775322231788,"user":{"displayName":"CoRE Stack","userId":"15795073488475210764"},"user_tz":-330},"id":"9zuvGfH4nwHB","outputId":"6eba4155-87dc-4a8b-ecdc-416edc167bd8"},"outputs":[{"output_type":"display_data","data":{"text/plain":[""],"text/html":["\n"," \n"," "]},"metadata":{}}],"source":["# Will take an AOI geometry as input and return the 10km x 10km grids in it as a list\n","def createGrids(aoi):\n"," proj = ee.Projection('EPSG:4326')\n"," gridSize = 10000\n"," grid = aoi.coveringGrid(proj, gridSize)\n"," features = grid.getInfo()['features']\n"," return features"]},{"cell_type":"code","execution_count":13,"metadata":{"colab":{"base_uri":"https://localhost:8080/","height":17},"executionInfo":{"elapsed":46,"status":"ok","timestamp":1775322233759,"user":{"displayName":"CoRE Stack","userId":"15795073488475210764"},"user_tz":-330},"id":"cHhG3vDgnzmb","outputId":"ed3905c8-8e3b-4179-c065-1b597e8d7750"},"outputs":[{"output_type":"display_data","data":{"text/plain":[""],"text/html":["\n"," \n"," "]},"metadata":{}}],"source":["def s2_mask(image):\n"," \"\"\"\n"," Getting a cloud-free Sentinel-2 imagery.\n"," \"\"\"\n"," quality_band = image.select('QA60')\n"," # Using the bit mask for clouds (bit 10) and cirrus clouds (bit 11) respectively.\n"," cloudmask = 1 << 10\n"," cirrusmask = 1 << 11\n"," # Both flags should be set to zero, indicating clear conditions of sky.\n"," mask = quality_band.bitwiseAnd(cloudmask).eq(0) and (quality_band.bitwiseAnd(cirrusmask).eq(0))\n"," return image.updateMask(mask)\n","\n","def get_s2_image(aoi, start_date, end_date):\n"," # s2_bands_season = [band + '_med_' + season for band in s2_bands]\n"," return ee.ImageCollection('COPERNICUS/S2_HARMONIZED').filterDate(\n"," start_date , end_date).filterBounds(aoi).filter(\n"," ee.Filter.lt(\"CLOUDY_PIXEL_PERCENTAGE\", 20)).sort('CLOUD_COVER').map(\n"," s2_mask).select(s2_bands).median().divide(10000).clip(aoi)\n","\n","def get_s1_image(aoi, start_date, end_date):\n"," # s1_bands_season = [band + '_' + season for band in s1_bands]\n"," return ee.ImageCollection('COPERNICUS/S1_GRD').filterDate(start_date , end_date).filterBounds(aoi).filter(\n"," ee.Filter.listContains('transmitterReceiverPolarisation', 'VV')).filter(\n"," ee.Filter.listContains('transmitterReceiverPolarisation', 'VH')).filter(\n"," ee.Filter.eq('instrumentMode', 'IW')).select(s1_bands).median().clip(aoi)"]},{"cell_type":"code","execution_count":14,"metadata":{"colab":{"base_uri":"https://localhost:8080/","height":17},"executionInfo":{"elapsed":6,"status":"ok","timestamp":1775322235773,"user":{"displayName":"CoRE Stack","userId":"15795073488475210764"},"user_tz":-330},"id":"KD4RzXrTn2CK","outputId":"e402cf2f-b589-4e45-dc83-92db5bf7d468"},"outputs":[{"output_type":"display_data","data":{"text/plain":[""],"text/html":["\n"," \n"," "]},"metadata":{}}],"source":["def save_data_csv(data_points, img_name, district, year):\n"," print(\"Saving data for\", district, year)\n"," new_img_name = img_name.replace('&', 'and')\n"," new_img_name = new_img_name.replace('(', '')\n"," new_img_name = new_img_name.replace(')', '')\n"," task = ee.batch.Export.table.toDrive(\n"," collection = data_points,\n"," description = new_img_name,\n"," folder = f\"{agroclimaticZone_acronym_dict[agroclimatic_zone]}_{year}\", # f\"{agroclimaticZone_acronym_dict[agroclimatic_zone]}_{district}_{year}\",\n"," fileNamePrefix = new_img_name,\n"," fileFormat = 'CSV'\n"," )\n"," task.start()\n"," print(\"Task Started\",task.status())\n"," return task"]},{"cell_type":"code","execution_count":23,"metadata":{"colab":{"base_uri":"https://localhost:8080/","height":53},"executionInfo":{"elapsed":19,"status":"ok","timestamp":1775323448485,"user":{"displayName":"CoRE Stack","userId":"15795073488475210764"},"user_tz":-330},"id":"X0eH_LS-n4j0","outputId":"c1686218-9aa1-4297-9d43-84fe8b1bc7f6"},"outputs":[{"output_type":"display_data","data":{"text/plain":[""],"text/html":["\n"," \n"," "]},"metadata":{}},{"output_type":"stream","name":"stdout","text":["1\n","['Y.S.R.']\n"]}],"source":["df = pd.read_csv(f'drive/MyDrive/TreeHealth/Agroclimatic_regions/{agroclimatic_zone}.csv')\n","dist_list = ['Y.S.R.']#list(df['Name'])\n","print(len(dist_list))\n","print(dist_list)"]},{"cell_type":"code","execution_count":24,"metadata":{"colab":{"base_uri":"https://localhost:8080/","height":35},"executionInfo":{"elapsed":6,"status":"ok","timestamp":1775323451067,"user":{"displayName":"CoRE Stack","userId":"15795073488475210764"},"user_tz":-330},"id":"pPZhyHc1n6zG","outputId":"fdc3a9aa-5b8c-4ebc-f944-9be9efa0e8bc"},"outputs":[{"output_type":"display_data","data":{"text/plain":[""],"text/html":["\n"," \n"," "]},"metadata":{}},{"output_type":"stream","name":"stdout","text":["['2015', '2016', '2017']\n"]}],"source":["years = [str(int(year)-2), str(int(year)-1), str(year)]\n","year_0 = years[0]\n","year_1 = years[1]\n","year_2 = years[2]\n","year_suffix = {year_0: year_0[-2:], year_1: year_1[-2:], year_2: year_2[-2:]}\n","print(years)"]},{"cell_type":"code","execution_count":17,"metadata":{"colab":{"base_uri":"https://localhost:8080/","height":17},"executionInfo":{"elapsed":7,"status":"ok","timestamp":1775322268138,"user":{"displayName":"CoRE Stack","userId":"15795073488475210764"},"user_tz":-330},"id":"QWVDbWyDn9NZ","outputId":"38eafe48-4777-45d0-9fdf-726318b7c2e8"},"outputs":[{"output_type":"display_data","data":{"text/plain":[""],"text/html":["\n"," \n"," "]},"metadata":{}}],"source":["def get_dw_tree_cover(aoi, start_date, end_date, scale = 25):\n"," tree_cover_dw = ee.ImageCollection(\"GOOGLE/DYNAMICWORLD/V1\").filterDate(start_date, end_date) \\\n"," .filterBounds(aoi).select('label').mode().clip(aoi)\n"," return tree_cover_dw.updateMask(tree_cover_dw.eq(1)).reproject(crs='EPSG:4326', scale=scale)\n","\n","def get_is_tree_cover(aoi, curr_year, scale = 25):\n"," curr_year = int(curr_year)\n"," indiasat_asset = f\"projects/corestack-datasets/assets/datasets/LULC_v3_river_basin/pan_india_lulc_v3_{curr_year}_{curr_year+1}\"\n"," lulc_image = ee.Image(indiasat_asset).select(\"predicted_label\").clip(aoi)\n"," return lulc_image.updateMask(lulc_image.eq(6)).reproject(crs='EPSG:4326', scale=scale)\n","\n","def get_tree_cover(aoi, curr_year, scale = 25):\n"," curr_year = int(curr_year)\n"," start_date = ee.Date(f'{curr_year}-07-01')\n"," end_date = ee.Date(f'{curr_year+1}-06-30')\n"," print(\"curr_year\", curr_year)\n"," tree_cover_is = get_is_tree_cover(aoi, curr_year, scale) if curr_year > 2016 else None\n"," tree_cover_dw = get_dw_tree_cover(aoi, start_date, end_date, scale)\n"," if tree_cover_is:\n"," tree_cover = tree_cover_is.mask().Or(tree_cover_dw.mask())\n"," else:\n"," print(\"=================Only DW\")\n"," tree_cover = tree_cover_dw.mask()\n"," # tree_cover = tree_cover_is.mask().Or(tree_cover_dw.mask()) if tree_cover_is else tree_cover_dw.mask()\n"," tree_cover = tree_cover.updateMask(tree_cover)\n"," return tree_cover.reproject(crs='EPSG:4326', scale=scale)"]},{"cell_type":"markdown","metadata":{"id":"LJEejVAPnvGR"},"source":["# Combined Grid"]},{"cell_type":"code","execution_count":25,"metadata":{"colab":{"base_uri":"https://localhost:8080/","height":1000},"id":"EhNLoTWcN3Ws","executionInfo":{"status":"ok","timestamp":1775323714437,"user_tz":-330,"elapsed":249336,"user":{"displayName":"CoRE Stack","userId":"15795073488475210764"}},"outputId":"cff2690a-c434-4c34-9ec2-af6aea4c75f7"},"outputs":[{"output_type":"display_data","data":{"text/plain":[""],"text/html":["\n"," \n"," "]},"metadata":{}},{"output_type":"stream","name":"stdout","text":["Year 2017, District 0: Y.S.R., grids: 201\n","curr_year 2017\n","Grid 0\n","Grid 1\n","Grid 2\n","Grid 3\n","Grid 4\n","Grid 5\n","Grid 6\n","Grid 7\n","Grid 8\n","Grid 9\n","Grid 10\n","Grid 11\n","Grid 12\n","Grid 13\n","Grid 14\n","Grid 15\n","Grid 16\n","Grid 17\n","Grid 18\n","Grid 19\n","Grid 20\n","Grid 21\n","Grid 22\n","Grid 23\n","Grid 24\n","Grid 25\n","Grid 26\n","Grid 27\n","Grid 28\n","Grid 29\n","Grid 30\n","Grid 31\n","Grid 32\n","Grid 33\n","Grid 34\n","Grid 35\n","Grid 36\n","Grid 37\n","Grid 38\n","Grid 39\n","Grid 40\n","Grid 41\n","Grid 42\n","Grid 43\n","Grid 44\n","Grid 45\n","Grid 46\n","Grid 47\n","Grid 48\n","Grid 49\n","Grid 50\n","Grid 51\n","Grid 52\n","Grid 53\n","Grid 54\n","Grid 55\n","Grid 56\n","Grid 57\n","Grid 58\n","Grid 59\n","Grid 60\n","Grid 61\n","Grid 62\n","Grid 63\n","Grid 64\n","Grid 65\n","Grid 66\n","Grid 67\n","Grid 68\n","Grid 69\n","Grid 70\n","Grid 71\n","Grid 72\n","Grid 73\n","Grid 74\n","Grid 75\n","Grid 76\n","Grid 77\n","Grid 78\n","Grid 79\n","Grid 80\n","Grid 81\n","Grid 82\n","Grid 83\n","Grid 84\n","Grid 85\n","Grid 86\n","Grid 87\n","Grid 88\n","Grid 89\n","Grid 90\n","Grid 91\n","Grid 92\n","Grid 93\n","Grid 94\n","Grid 95\n","Grid 96\n","Grid 97\n","Grid 98\n","Grid 99\n","Grid 100\n","Grid 101\n","Grid 102\n","Grid 103\n","Grid 104\n","Grid 105\n","Grid 106\n","Grid 107\n","Grid 108\n","Grid 109\n","Grid 110\n","Grid 111\n","Grid 112\n","Grid 113\n","Grid 114\n","Grid 115\n","Grid 116\n","Grid 117\n","Grid 118\n","Grid 119\n","Grid 120\n","Grid 121\n","Grid 122\n","Grid 123\n","Grid 124\n","Grid 125\n","Grid 126\n","Grid 127\n","Grid 128\n","Grid 129\n","Grid 130\n","Grid 131\n","Grid 132\n","Grid 133\n","Grid 134\n","Grid 135\n","Grid 136\n","Grid 137\n","Grid 138\n","Grid 139\n","Grid 140\n","Grid 141\n","Grid 142\n","Grid 143\n","Grid 144\n","Grid 145\n","Grid 146\n","Grid 147\n","Grid 148\n","Grid 149\n","Grid 150\n","Grid 151\n","Grid 152\n","Grid 153\n","Grid 154\n","Grid 155\n","Grid 156\n","Grid 157\n","Grid 158\n","Grid 159\n","Grid 160\n","Grid 161\n","Grid 162\n","Grid 163\n","Grid 164\n","Grid 165\n","Grid 166\n","Grid 167\n","Grid 168\n","Grid 169\n","Grid 170\n","Grid 171\n","Grid 172\n","Grid 173\n","Grid 174\n","Grid 175\n","Grid 176\n","Grid 177\n","Grid 178\n","Grid 179\n","Grid 180\n","Grid 181\n","Grid 182\n","Grid 183\n","Grid 184\n","Grid 185\n","Grid 186\n","Grid 187\n","Grid 188\n","Grid 189\n","Grid 190\n","Grid 191\n","Grid 192\n","Grid 193\n","Grid 194\n","Grid 195\n","Grid 196\n","Grid 197\n","Grid 198\n","Grid 199\n","Grid 200\n","[, , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , ]\n","Saving data for Y.S.R. 2017\n","Task Started {'state': 'READY', 'description': 'Y.S.R._2017_all_grids', 'priority': 100, 'creation_timestamp_ms': 1775323714046, 'update_timestamp_ms': 1775323714046, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'K7HMZ3QXN7RJ4OVN5RGYFIAY', 'name': 'projects/ext-datasets/operations/K7HMZ3QXN7RJ4OVN5RGYFIAY'}\n","249.2729194164276\n"]}],"source":["# Combined tiles and optimized\n","import sys\n","sys.setrecursionlimit(6000)\n","for curr_year in ['2017']:#years:\n","\n"," total_time = 0\n","\n"," dist_cnt = 0\n"," for district in dist_list:\n","\n"," start_time = time.time()\n","\n"," district_aoi = india_district_boundary.filter(ee.Filter.eq('Name', district)).geometry()\n"," district_aoi = district_aoi.intersection(agrozone)\n"," features = createGrids(district_aoi)\n","\n"," print(f'Year {curr_year}, District {dist_cnt}: {district}, grids: {len(features)}')\n","\n"," path = f'drive/MyDrive/TreeHealth/{agroclimatic_zone}/{district}/{curr_year}/'\n"," os.makedirs(path, exist_ok=True)\n","\n"," # df = pd.DataFrame()\n"," # df['grid_num'] = list(range(len(features)))\n"," valid_grid_indices = []\n"," tree_points_list = []\n","\n"," # Precompute tree cover for the whole district\n"," tree_cover_district = get_tree_cover(district_aoi, curr_year, scale=25)\n","\n"," # Precompute S1 and S2 images for the whole district for each season\n"," start_date = START_DATE[int(curr_year)]\n"," end_date = END_DATE[int(curr_year)]\n"," s1_images = {}\n"," s2_images = {}\n"," for season in ['kharif', 'rabi', 'zaid']:\n"," try:\n"," s1_images[season] = get_s1_image(district_aoi, start_date[season], end_date[season]).updateMask(tree_cover_district)\n"," except Exception as exp:\n"," print(f\"S1 Error occured: {season}\", exp)\n"," s1_images[season] = None\n"," try:\n"," s2_images[season] = get_s2_image(district_aoi, start_date[season], end_date[season]).updateMask(tree_cover_district)\n"," except Exception as exp:\n"," print(f\"S2 Error occured: {season}\", exp)\n"," s2_images[season] = None\n","\n"," # List to hold all sample points\n"," all_sample_points = []\n","\n"," i = 0\n"," for feature in features:\n"," print(f'Grid {i}')\n","\n"," coord = feature['geometry']['coordinates'][0]\n"," aoi = ee.Geometry.Polygon(coord)\n"," aoi = aoi.intersection(district_aoi)\n"," # Get the coordinates of the geometry\n"," coordinates = aoi.coordinates()\n"," # Check if coordinates list is empty (i.e. geometry is empty)\n"," is_empty = coordinates.length().eq(0)\n","\n"," # print('Is geometry empty?', is_empty.getInfo())\n"," if not is_empty.getInfo():\n"," valid_grid_indices.append(i) # Only add if valid\n"," img_name = district + \"_\" + str(i) + \"_\" + str(curr_year)\n","\n"," # Clip tree cover to grid\n"," tree_cover = tree_cover_district.clip(aoi)\n","\n"," # Compose image for all seasons using precomputed images\n"," image = None\n"," if s1_images['kharif'] is not None:\n"," image = s1_images['kharif'].clip(aoi)\n"," if s2_images['kharif'] is not None and image is not None:\n"," s2_data = s2_images['kharif'].clip(aoi)\n"," image = image.addBands(s2_data).select(s1_bands + s2_bands)\n"," image = image.rename([band + '_kharif' for band in s1_bands + s2_bands])\n","\n"," for season in ['rabi', 'zaid']:\n"," s1_data = s1_images[season]\n"," s2_data = s2_images[season]\n"," if s1_data is not None:\n"," s1_data = s1_data.clip(aoi)\n"," if s2_data is not None and s1_data is not None:\n"," s2_data = s2_data.clip(aoi)\n"," image_merged = s1_data.addBands(s2_data).select(s1_bands + s2_bands)\n"," image_merged = image_merged.rename([band + '_' + season for band in s1_bands + s2_bands])\n"," image = image.addBands(image_merged) if image is not None else image_merged\n","\n"," # Sample points only if tree cover exists\n"," sample_tree_cover = tree_cover.sample(\n"," region=aoi,\n"," scale=25,\n"," factor=1,\n"," tileScale=10,\n"," geometries=True\n"," )\n"," try:\n"," tree_points = sample_tree_cover.size().getInfo()\n"," except:\n"," tree_points = 0\n"," tree_points_list.append(tree_points)\n","\n"," # if tree_points > 0 and image is not None:\n"," sample_points = image.sample(\n"," region=aoi,\n"," scale=25,\n"," factor=1,\n"," tileScale=10,\n"," geometries=True\n"," )\n"," all_sample_points.append(sample_points)\n","\n"," i += 1\n"," print(all_sample_points)\n"," # Merge all sample points into a single FeatureCollection\n"," if all_sample_points:\n"," merged_sample_points = all_sample_points[0]\n"," for sp in all_sample_points[1:]:\n"," merged_sample_points = merged_sample_points.merge(sp)\n","\n"," # Export the merged FeatureCollection\n"," try:\n"," img_name = district + \"_\" + str(curr_year) + \"_all_grids\"\n"," task = save_data_csv(merged_sample_points, img_name, district, curr_year)\n"," prev_task = task\n"," except Exception as e:\n"," print(e)\n"," while prev_task.status()['state'] != 'COMPLETED' and prev_task.status()['state'] != 'FAILED':\n"," continue\n"," task = save_data_csv(merged_sample_points, img_name, path, curr_year)\n"," prev_task = task\n","\n"," df = pd.DataFrame()\n"," df['grid_num'] = valid_grid_indices\n"," df['tree_points'] = tree_points_list\n"," df.to_csv(path + 'tree_cover_points.csv', index=False)\n","\n"," dist_cnt += 1\n"," end_time = time.time()\n","\n"," total_time += (end_time - start_time)\n"," print(total_time)\n","\n"," # print(\"Waiting for last task to be completed...\")\n"," # while prev_task.status()['state'] != 'COMPLETED' and prev_task.status()['state'] != 'FAILED':\n"," # continue\n"," # print(\"Last task completed!\")\n","\n"," # total_time += (time.time() - end_time)\n"," # print(\"Total Time Taken:\", total_time)"]},{"cell_type":"markdown","metadata":{"id":"snxgyeoRno-K"},"source":["# Separate Grid (Older version - Do not run this)"]},{"cell_type":"code","execution_count":null,"metadata":{"colab":{"base_uri":"https://localhost:8080/","height":1000},"id":"P1WY_Cb7oAgz","outputId":"a7c02335-532e-4b97-820f-6361ad46ffb9"},"outputs":[{"data":{"text/html":["\n"," \n"," "],"text/plain":[""]},"metadata":{},"output_type":"display_data"},{"name":"stdout","output_type":"stream","text":["\u001b[1;30;43mStreaming output truncated to the last 5000 lines.\u001b[0m\n","Task Started {'state': 'READY', 'description': 'Haora_15_2017', 'priority': 100, 'creation_timestamp_ms': 1762652761544, 'update_timestamp_ms': 1762652761544, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'JPA4UPIX3U5VCIKQ2SWCQECH', 'name': 'projects/ext-datasets/operations/JPA4UPIX3U5VCIKQ2SWCQECH'}\n","Grid 16\n","curr_year 2017\n","Saving data for Haora 2017\n","Task Started {'state': 'READY', 'description': 'Haora_16_2017', 'priority': 100, 'creation_timestamp_ms': 1762652766812, 'update_timestamp_ms': 1762652766812, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'EAMYVBQXNMMEY4FOMWM43BNC', 'name': 'projects/ext-datasets/operations/EAMYVBQXNMMEY4FOMWM43BNC'}\n","Grid 17\n","curr_year 2017\n","Saving data for Haora 2017\n","Task Started {'state': 'READY', 'description': 'Haora_17_2017', 'priority': 100, 'creation_timestamp_ms': 1762652773310, 'update_timestamp_ms': 1762652773310, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'XJ6UW3VLPSK622S7N3GTPBDE', 'name': 'projects/ext-datasets/operations/XJ6UW3VLPSK622S7N3GTPBDE'}\n","Grid 18\n","curr_year 2017\n","Saving data for Haora 2017\n","Task Started {'state': 'READY', 'description': 'Haora_18_2017', 'priority': 100, 'creation_timestamp_ms': 1762652779471, 'update_timestamp_ms': 1762652779471, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'UQF6ZXFYNJIJDDAVOJAADO4Q', 'name': 'projects/ext-datasets/operations/UQF6ZXFYNJIJDDAVOJAADO4Q'}\n","Grid 19\n","curr_year 2017\n","Saving data for Haora 2017\n","Task Started {'state': 'READY', 'description': 'Haora_19_2017', 'priority': 100, 'creation_timestamp_ms': 1762652788203, 'update_timestamp_ms': 1762652788203, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'V6CMFXQIIPPHMDY7G7RSBHXO', 'name': 'projects/ext-datasets/operations/V6CMFXQIIPPHMDY7G7RSBHXO'}\n","Grid 20\n","curr_year 2017\n","Saving data for Haora 2017\n","Task Started {'state': 'READY', 'description': 'Haora_20_2017', 'priority': 100, 'creation_timestamp_ms': 1762652795968, 'update_timestamp_ms': 1762652795968, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'IEIRR7HN3564PNRHXV3B7LBO', 'name': 'projects/ext-datasets/operations/IEIRR7HN3564PNRHXV3B7LBO'}\n","Grid 21\n","curr_year 2017\n","Saving data for Haora 2017\n","Task Started {'state': 'READY', 'description': 'Haora_21_2017', 'priority': 100, 'creation_timestamp_ms': 1762652804392, 'update_timestamp_ms': 1762652804392, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'OJ627QLSFNOELKQGFC4XWVXJ', 'name': 'projects/ext-datasets/operations/OJ627QLSFNOELKQGFC4XWVXJ'}\n","Grid 22\n","curr_year 2017\n","Saving data for Haora 2017\n","Task Started {'state': 'READY', 'description': 'Haora_22_2017', 'priority': 100, 'creation_timestamp_ms': 1762652812461, 'update_timestamp_ms': 1762652812461, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'HVAIQPOGNWFJXL4P2GKTW6CP', 'name': 'projects/ext-datasets/operations/HVAIQPOGNWFJXL4P2GKTW6CP'}\n","Grid 23\n","curr_year 2017\n","Saving data for Haora 2017\n","Task Started {'state': 'READY', 'description': 'Haora_23_2017', 'priority': 100, 'creation_timestamp_ms': 1762652821455, 'update_timestamp_ms': 1762652821455, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '7Z6YDDABUKVAF6WCB57HEL67', 'name': 'projects/ext-datasets/operations/7Z6YDDABUKVAF6WCB57HEL67'}\n","Grid 24\n","curr_year 2017\n","Saving data for Haora 2017\n","Task Started {'state': 'READY', 'description': 'Haora_24_2017', 'priority': 100, 'creation_timestamp_ms': 1762652829407, 'update_timestamp_ms': 1762652829407, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'OMGDWCBDMII3R7MTIP5A46NM', 'name': 'projects/ext-datasets/operations/OMGDWCBDMII3R7MTIP5A46NM'}\n","Grid 25\n","curr_year 2017\n","Saving data for Haora 2017\n","Task Started {'state': 'READY', 'description': 'Haora_25_2017', 'priority': 100, 'creation_timestamp_ms': 1762652837188, 'update_timestamp_ms': 1762652837188, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'ZGIKQF5DUYPD5IC6PHGBDRZ4', 'name': 'projects/ext-datasets/operations/ZGIKQF5DUYPD5IC6PHGBDRZ4'}\n","Grid 26\n","curr_year 2017\n","Saving data for Haora 2017\n","Task Started {'state': 'READY', 'description': 'Haora_26_2017', 'priority': 100, 'creation_timestamp_ms': 1762652841386, 'update_timestamp_ms': 1762652841386, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'P6D6LB352XNF2GB6FND34XKL', 'name': 'projects/ext-datasets/operations/P6D6LB352XNF2GB6FND34XKL'}\n","Grid 27\n","curr_year 2017\n","Saving data for Haora 2017\n","Task Started {'state': 'READY', 'description': 'Haora_27_2017', 'priority': 100, 'creation_timestamp_ms': 1762652847414, 'update_timestamp_ms': 1762652847414, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'XU2OBEAFI2SP46DPZFU2Q724', 'name': 'projects/ext-datasets/operations/XU2OBEAFI2SP46DPZFU2Q724'}\n","Grid 28\n","curr_year 2017\n","Saving data for Haora 2017\n","Task Started {'state': 'READY', 'description': 'Haora_28_2017', 'priority': 100, 'creation_timestamp_ms': 1762652853805, 'update_timestamp_ms': 1762652853805, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'R26FGY4WBDEUAU5ISWRZ6XQH', 'name': 'projects/ext-datasets/operations/R26FGY4WBDEUAU5ISWRZ6XQH'}\n","Grid 29\n","curr_year 2017\n","Saving data for Haora 2017\n","Task Started {'state': 'READY', 'description': 'Haora_29_2017', 'priority': 100, 'creation_timestamp_ms': 1762652861699, 'update_timestamp_ms': 1762652861699, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'N33BZBXVFCNZQAF6T2NMZDY7', 'name': 'projects/ext-datasets/operations/N33BZBXVFCNZQAF6T2NMZDY7'}\n","3315.372728586197\n","Year 2017, District 17: Hugli, grids: 55\n","Grid 0\n","curr_year 2017\n","Saving data for Hugli 2017\n","Task Started {'state': 'READY', 'description': 'Hugli_0_2017', 'priority': 100, 'creation_timestamp_ms': 1762652873437, 'update_timestamp_ms': 1762652873437, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '67WLQUZFYL335CGJ7LLBYJLI', 'name': 'projects/ext-datasets/operations/67WLQUZFYL335CGJ7LLBYJLI'}\n","Grid 1\n","curr_year 2017\n","Saving data for Hugli 2017\n","Task Started {'state': 'READY', 'description': 'Hugli_1_2017', 'priority': 100, 'creation_timestamp_ms': 1762652879133, 'update_timestamp_ms': 1762652879133, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'HW36MCMET6I4Y5UNARSAIH3V', 'name': 'projects/ext-datasets/operations/HW36MCMET6I4Y5UNARSAIH3V'}\n","Grid 2\n","curr_year 2017\n","Saving data for Hugli 2017\n","Task Started {'state': 'READY', 'description': 'Hugli_2_2017', 'priority': 100, 'creation_timestamp_ms': 1762652886436, 'update_timestamp_ms': 1762652886436, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'IWZBIZESVKRK7M56GH3YEWY6', 'name': 'projects/ext-datasets/operations/IWZBIZESVKRK7M56GH3YEWY6'}\n","Grid 3\n","curr_year 2017\n","Saving data for Hugli 2017\n","Task Started {'state': 'READY', 'description': 'Hugli_3_2017', 'priority': 100, 'creation_timestamp_ms': 1762652894869, 'update_timestamp_ms': 1762652894869, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'JLIX56WRXKWEHW44N55XWRKV', 'name': 'projects/ext-datasets/operations/JLIX56WRXKWEHW44N55XWRKV'}\n","Grid 4\n","curr_year 2017\n","Saving data for Hugli 2017\n","Task Started {'state': 'READY', 'description': 'Hugli_4_2017', 'priority': 100, 'creation_timestamp_ms': 1762652902789, 'update_timestamp_ms': 1762652902789, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'MM5SISFPENEPZOVVAFMJUVU5', 'name': 'projects/ext-datasets/operations/MM5SISFPENEPZOVVAFMJUVU5'}\n","Grid 5\n","curr_year 2017\n","Saving data for Hugli 2017\n","Task Started {'state': 'READY', 'description': 'Hugli_5_2017', 'priority': 100, 'creation_timestamp_ms': 1762652910673, 'update_timestamp_ms': 1762652910673, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'KQIK775U7IYSBN5LZJYJD5IS', 'name': 'projects/ext-datasets/operations/KQIK775U7IYSBN5LZJYJD5IS'}\n","Grid 6\n","curr_year 2017\n","Saving data for Hugli 2017\n","Task Started {'state': 'READY', 'description': 'Hugli_6_2017', 'priority': 100, 'creation_timestamp_ms': 1762652917226, 'update_timestamp_ms': 1762652917226, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'DCVEK3WW2CGFFIV7FKQHXJNN', 'name': 'projects/ext-datasets/operations/DCVEK3WW2CGFFIV7FKQHXJNN'}\n","Grid 7\n","curr_year 2017\n","Saving data for Hugli 2017\n","Task Started {'state': 'READY', 'description': 'Hugli_7_2017', 'priority': 100, 'creation_timestamp_ms': 1762652924007, 'update_timestamp_ms': 1762652924007, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'YEYMUF2WW3TGZHOQRWRAFT54', 'name': 'projects/ext-datasets/operations/YEYMUF2WW3TGZHOQRWRAFT54'}\n","Grid 8\n","curr_year 2017\n","Saving data for Hugli 2017\n","Task Started {'state': 'READY', 'description': 'Hugli_8_2017', 'priority': 100, 'creation_timestamp_ms': 1762652931314, 'update_timestamp_ms': 1762652931314, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'BDZGPIYOXGCCFOWADOVDSNHC', 'name': 'projects/ext-datasets/operations/BDZGPIYOXGCCFOWADOVDSNHC'}\n","Grid 9\n","curr_year 2017\n","Saving data for Hugli 2017\n","Task Started {'state': 'READY', 'description': 'Hugli_9_2017', 'priority': 100, 'creation_timestamp_ms': 1762652937745, 'update_timestamp_ms': 1762652937745, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'WUG6PNYVFDMWPJSYSVORFPFD', 'name': 'projects/ext-datasets/operations/WUG6PNYVFDMWPJSYSVORFPFD'}\n","Grid 10\n","curr_year 2017\n","Saving data for Hugli 2017\n","Task Started {'state': 'READY', 'description': 'Hugli_10_2017', 'priority': 100, 'creation_timestamp_ms': 1762652945240, 'update_timestamp_ms': 1762652945240, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '7MHCLX37JCBBJX7QP2MRKO2X', 'name': 'projects/ext-datasets/operations/7MHCLX37JCBBJX7QP2MRKO2X'}\n","Grid 11\n","curr_year 2017\n","Saving data for Hugli 2017\n","Task Started {'state': 'READY', 'description': 'Hugli_11_2017', 'priority': 100, 'creation_timestamp_ms': 1762652952393, 'update_timestamp_ms': 1762652952393, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'RCXTUBPSVBBZTZDUILO7SCOE', 'name': 'projects/ext-datasets/operations/RCXTUBPSVBBZTZDUILO7SCOE'}\n","Grid 12\n","curr_year 2017\n","Saving data for Hugli 2017\n","Task Started {'state': 'READY', 'description': 'Hugli_12_2017', 'priority': 100, 'creation_timestamp_ms': 1762652959856, 'update_timestamp_ms': 1762652959856, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'UU4US5LIPZLFY3HCI3XRKRSG', 'name': 'projects/ext-datasets/operations/UU4US5LIPZLFY3HCI3XRKRSG'}\n","Grid 13\n","curr_year 2017\n","Saving data for Hugli 2017\n","Task Started {'state': 'READY', 'description': 'Hugli_13_2017', 'priority': 100, 'creation_timestamp_ms': 1762652967985, 'update_timestamp_ms': 1762652967985, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'CEHVDOTQNQOWS7Z36OTLHHIB', 'name': 'projects/ext-datasets/operations/CEHVDOTQNQOWS7Z36OTLHHIB'}\n","Grid 14\n","curr_year 2017\n","Saving data for Hugli 2017\n","Task Started {'state': 'READY', 'description': 'Hugli_14_2017', 'priority': 100, 'creation_timestamp_ms': 1762652975340, 'update_timestamp_ms': 1762652975340, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'ND7ZK45V2GPRHW572HNNOUNP', 'name': 'projects/ext-datasets/operations/ND7ZK45V2GPRHW572HNNOUNP'}\n","Grid 15\n","curr_year 2017\n","Saving data for Hugli 2017\n","Task Started {'state': 'READY', 'description': 'Hugli_15_2017', 'priority': 100, 'creation_timestamp_ms': 1762652983473, 'update_timestamp_ms': 1762652983473, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'N4DYL3R2WRHR6VK7LGYGAHAE', 'name': 'projects/ext-datasets/operations/N4DYL3R2WRHR6VK7LGYGAHAE'}\n","Grid 16\n","curr_year 2017\n","Saving data for Hugli 2017\n","Task Started {'state': 'READY', 'description': 'Hugli_16_2017', 'priority': 100, 'creation_timestamp_ms': 1762652990247, 'update_timestamp_ms': 1762652990247, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'ILZGJKW7WCP6GQPD46H65GKL', 'name': 'projects/ext-datasets/operations/ILZGJKW7WCP6GQPD46H65GKL'}\n","Grid 17\n","curr_year 2017\n","Saving data for Hugli 2017\n","Task Started {'state': 'READY', 'description': 'Hugli_17_2017', 'priority': 100, 'creation_timestamp_ms': 1762652996402, 'update_timestamp_ms': 1762652996402, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'S3O4QNBG6YIUR7IVRGMUA2UI', 'name': 'projects/ext-datasets/operations/S3O4QNBG6YIUR7IVRGMUA2UI'}\n","Grid 18\n","curr_year 2017\n","Saving data for Hugli 2017\n","Task Started {'state': 'READY', 'description': 'Hugli_18_2017', 'priority': 100, 'creation_timestamp_ms': 1762653004523, 'update_timestamp_ms': 1762653004523, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '7WEN5MKBMTCYRO73BRTBNMLD', 'name': 'projects/ext-datasets/operations/7WEN5MKBMTCYRO73BRTBNMLD'}\n","Grid 19\n","curr_year 2017\n","Saving data for Hugli 2017\n","Task Started {'state': 'READY', 'description': 'Hugli_19_2017', 'priority': 100, 'creation_timestamp_ms': 1762653010473, 'update_timestamp_ms': 1762653010473, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'OFXUFGKTKWLLC2U3ACD4V4PE', 'name': 'projects/ext-datasets/operations/OFXUFGKTKWLLC2U3ACD4V4PE'}\n","Grid 20\n","curr_year 2017\n","Saving data for Hugli 2017\n","Task Started {'state': 'READY', 'description': 'Hugli_20_2017', 'priority': 100, 'creation_timestamp_ms': 1762653019182, 'update_timestamp_ms': 1762653019182, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'VUAUUFFV2SAVVOEXQLALAWXR', 'name': 'projects/ext-datasets/operations/VUAUUFFV2SAVVOEXQLALAWXR'}\n","Grid 21\n","curr_year 2017\n","Saving data for Hugli 2017\n","Task Started {'state': 'READY', 'description': 'Hugli_21_2017', 'priority': 100, 'creation_timestamp_ms': 1762653024959, 'update_timestamp_ms': 1762653024959, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'IH2FDQWZWDCAGW6JRZTZP7QI', 'name': 'projects/ext-datasets/operations/IH2FDQWZWDCAGW6JRZTZP7QI'}\n","Grid 22\n","curr_year 2017\n","Saving data for Hugli 2017\n","Task Started {'state': 'READY', 'description': 'Hugli_22_2017', 'priority': 100, 'creation_timestamp_ms': 1762653032654, 'update_timestamp_ms': 1762653032654, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'HE5ZQHMEEPRCZZ2QCXUN752R', 'name': 'projects/ext-datasets/operations/HE5ZQHMEEPRCZZ2QCXUN752R'}\n","Grid 23\n","curr_year 2017\n","Saving data for Hugli 2017\n","Task Started {'state': 'READY', 'description': 'Hugli_23_2017', 'priority': 100, 'creation_timestamp_ms': 1762653039615, 'update_timestamp_ms': 1762653039615, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'YHSIU2LEF6WYMG6VW3ZCDMIX', 'name': 'projects/ext-datasets/operations/YHSIU2LEF6WYMG6VW3ZCDMIX'}\n","Grid 24\n","curr_year 2017\n","Saving data for Hugli 2017\n","Task Started {'state': 'READY', 'description': 'Hugli_24_2017', 'priority': 100, 'creation_timestamp_ms': 1762653046808, 'update_timestamp_ms': 1762653046808, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'KVZWQHJVKSAWJ3AHZMNXIJOV', 'name': 'projects/ext-datasets/operations/KVZWQHJVKSAWJ3AHZMNXIJOV'}\n","Grid 25\n","curr_year 2017\n","Saving data for Hugli 2017\n","Task Started {'state': 'READY', 'description': 'Hugli_25_2017', 'priority': 100, 'creation_timestamp_ms': 1762653052031, 'update_timestamp_ms': 1762653052031, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'SVCS6CUVAMGD3BIXWJ2O4DFP', 'name': 'projects/ext-datasets/operations/SVCS6CUVAMGD3BIXWJ2O4DFP'}\n","Grid 26\n","curr_year 2017\n","Saving data for Hugli 2017\n","Task Started {'state': 'READY', 'description': 'Hugli_26_2017', 'priority': 100, 'creation_timestamp_ms': 1762653060075, 'update_timestamp_ms': 1762653060075, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'ZHOWVC7Q2MNYEQWLZBH7RWTQ', 'name': 'projects/ext-datasets/operations/ZHOWVC7Q2MNYEQWLZBH7RWTQ'}\n","Grid 27\n","curr_year 2017\n","Saving data for Hugli 2017\n","Task Started {'state': 'READY', 'description': 'Hugli_27_2017', 'priority': 100, 'creation_timestamp_ms': 1762653068196, 'update_timestamp_ms': 1762653068196, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'LVE2GEXSXRTJGXVWGNFWRXBZ', 'name': 'projects/ext-datasets/operations/LVE2GEXSXRTJGXVWGNFWRXBZ'}\n","Grid 28\n","curr_year 2017\n","Saving data for Hugli 2017\n","Task Started {'state': 'READY', 'description': 'Hugli_28_2017', 'priority': 100, 'creation_timestamp_ms': 1762653075759, 'update_timestamp_ms': 1762653075759, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'PQUFVZ6HJLZ3KKTMJVX7MYEI', 'name': 'projects/ext-datasets/operations/PQUFVZ6HJLZ3KKTMJVX7MYEI'}\n","Grid 29\n","curr_year 2017\n","Saving data for Hugli 2017\n","Task Started {'state': 'READY', 'description': 'Hugli_29_2017', 'priority': 100, 'creation_timestamp_ms': 1762653083680, 'update_timestamp_ms': 1762653083680, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'MWXGACU43LGFK4XXLAPTLJXX', 'name': 'projects/ext-datasets/operations/MWXGACU43LGFK4XXLAPTLJXX'}\n","Grid 30\n","curr_year 2017\n","Saving data for Hugli 2017\n","Task Started {'state': 'READY', 'description': 'Hugli_30_2017', 'priority': 100, 'creation_timestamp_ms': 1762653087724, 'update_timestamp_ms': 1762653087724, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'FYBTXGKGIUVM2BP3FFGZZCNO', 'name': 'projects/ext-datasets/operations/FYBTXGKGIUVM2BP3FFGZZCNO'}\n","Grid 31\n","curr_year 2017\n","Saving data for Hugli 2017\n","Task Started {'state': 'READY', 'description': 'Hugli_31_2017', 'priority': 100, 'creation_timestamp_ms': 1762653094613, 'update_timestamp_ms': 1762653094613, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'IWA6Q2SLUATTOPA5Z3QCXMNP', 'name': 'projects/ext-datasets/operations/IWA6Q2SLUATTOPA5Z3QCXMNP'}\n","Grid 32\n","curr_year 2017\n","Saving data for Hugli 2017\n","Task Started {'state': 'READY', 'description': 'Hugli_32_2017', 'priority': 100, 'creation_timestamp_ms': 1762653098875, 'update_timestamp_ms': 1762653098875, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'ZAH4CPIQF53TIOOR25RQY3UE', 'name': 'projects/ext-datasets/operations/ZAH4CPIQF53TIOOR25RQY3UE'}\n","Grid 33\n","curr_year 2017\n","Saving data for Hugli 2017\n","Task Started {'state': 'READY', 'description': 'Hugli_33_2017', 'priority': 100, 'creation_timestamp_ms': 1762653106035, 'update_timestamp_ms': 1762653106035, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'EU4I3HM7SH64GU5IVPGJQBFM', 'name': 'projects/ext-datasets/operations/EU4I3HM7SH64GU5IVPGJQBFM'}\n","Grid 34\n","curr_year 2017\n","Saving data for Hugli 2017\n","Task Started {'state': 'READY', 'description': 'Hugli_34_2017', 'priority': 100, 'creation_timestamp_ms': 1762653113073, 'update_timestamp_ms': 1762653113073, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'PWUBHH3Q7PPTLFBFLKRYSYUC', 'name': 'projects/ext-datasets/operations/PWUBHH3Q7PPTLFBFLKRYSYUC'}\n","Grid 35\n","curr_year 2017\n","Saving data for Hugli 2017\n","Task Started {'state': 'READY', 'description': 'Hugli_35_2017', 'priority': 100, 'creation_timestamp_ms': 1762653120645, 'update_timestamp_ms': 1762653120645, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'H7T2JZOKUCFFV2NW2RIJ56II', 'name': 'projects/ext-datasets/operations/H7T2JZOKUCFFV2NW2RIJ56II'}\n","Grid 36\n","curr_year 2017\n","Saving data for Hugli 2017\n","Task Started {'state': 'READY', 'description': 'Hugli_36_2017', 'priority': 100, 'creation_timestamp_ms': 1762653126866, 'update_timestamp_ms': 1762653126866, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'VNCXOPJMS62XSDOAGVHP4O73', 'name': 'projects/ext-datasets/operations/VNCXOPJMS62XSDOAGVHP4O73'}\n","Grid 37\n","curr_year 2017\n","Saving data for Hugli 2017\n","Task Started {'state': 'READY', 'description': 'Hugli_37_2017', 'priority': 100, 'creation_timestamp_ms': 1762653133620, 'update_timestamp_ms': 1762653133620, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'UDONOS7G2HIGSIVAC2GP4ME7', 'name': 'projects/ext-datasets/operations/UDONOS7G2HIGSIVAC2GP4ME7'}\n","Grid 38\n","curr_year 2017\n","Saving data for Hugli 2017\n","Task Started {'state': 'READY', 'description': 'Hugli_38_2017', 'priority': 100, 'creation_timestamp_ms': 1762653138488, 'update_timestamp_ms': 1762653138488, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'G5ELJ4O53QEHVQY2Y7WNKXNM', 'name': 'projects/ext-datasets/operations/G5ELJ4O53QEHVQY2Y7WNKXNM'}\n","Grid 39\n","curr_year 2017\n","Saving data for Hugli 2017\n","Task Started {'state': 'READY', 'description': 'Hugli_39_2017', 'priority': 100, 'creation_timestamp_ms': 1762653144956, 'update_timestamp_ms': 1762653144956, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'V665E4OPIGTCWB5FO4K43HNC', 'name': 'projects/ext-datasets/operations/V665E4OPIGTCWB5FO4K43HNC'}\n","Grid 40\n","curr_year 2017\n","Saving data for Hugli 2017\n","Task Started {'state': 'READY', 'description': 'Hugli_40_2017', 'priority': 100, 'creation_timestamp_ms': 1762653151869, 'update_timestamp_ms': 1762653151869, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'ATIMLT6LUYHI6PNG4EVNNSNG', 'name': 'projects/ext-datasets/operations/ATIMLT6LUYHI6PNG4EVNNSNG'}\n","Grid 41\n","curr_year 2017\n","Saving data for Hugli 2017\n","Task Started {'state': 'READY', 'description': 'Hugli_41_2017', 'priority': 100, 'creation_timestamp_ms': 1762653159135, 'update_timestamp_ms': 1762653159135, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'PFM3F32LPGML42REWDXLJ5YQ', 'name': 'projects/ext-datasets/operations/PFM3F32LPGML42REWDXLJ5YQ'}\n","Grid 42\n","curr_year 2017\n","Saving data for Hugli 2017\n","Task Started {'state': 'READY', 'description': 'Hugli_42_2017', 'priority': 100, 'creation_timestamp_ms': 1762653163523, 'update_timestamp_ms': 1762653163523, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'OOX27I5RUBHMDS3ZKCZKDHJM', 'name': 'projects/ext-datasets/operations/OOX27I5RUBHMDS3ZKCZKDHJM'}\n","Grid 43\n","curr_year 2017\n","Saving data for Hugli 2017\n","Task Started {'state': 'READY', 'description': 'Hugli_43_2017', 'priority': 100, 'creation_timestamp_ms': 1762653167557, 'update_timestamp_ms': 1762653167557, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'LD4RYM7I2ZWAREDALCSB5SVH', 'name': 'projects/ext-datasets/operations/LD4RYM7I2ZWAREDALCSB5SVH'}\n","Grid 44\n","curr_year 2017\n","Saving data for Hugli 2017\n","Task Started {'state': 'READY', 'description': 'Hugli_44_2017', 'priority': 100, 'creation_timestamp_ms': 1762653171076, 'update_timestamp_ms': 1762653171076, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'UBAJS5UUTC7LA7XQWDPYGEDV', 'name': 'projects/ext-datasets/operations/UBAJS5UUTC7LA7XQWDPYGEDV'}\n","Grid 45\n","curr_year 2017\n","Saving data for Hugli 2017\n","Task Started {'state': 'READY', 'description': 'Hugli_45_2017', 'priority': 100, 'creation_timestamp_ms': 1762653177390, 'update_timestamp_ms': 1762653177390, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'BAXAKZQKWVWRIMXKNU5NGIGI', 'name': 'projects/ext-datasets/operations/BAXAKZQKWVWRIMXKNU5NGIGI'}\n","Grid 46\n","curr_year 2017\n","Saving data for Hugli 2017\n","Task Started {'state': 'READY', 'description': 'Hugli_46_2017', 'priority': 100, 'creation_timestamp_ms': 1762653187101, 'update_timestamp_ms': 1762653187101, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '62JU7POKQPOCQ6ECPZBRZOJK', 'name': 'projects/ext-datasets/operations/62JU7POKQPOCQ6ECPZBRZOJK'}\n","Grid 47\n","curr_year 2017\n","Saving data for Hugli 2017\n","Task Started {'state': 'READY', 'description': 'Hugli_47_2017', 'priority': 100, 'creation_timestamp_ms': 1762653195208, 'update_timestamp_ms': 1762653195208, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'BBM5LULHPY57D26QENGBFZEA', 'name': 'projects/ext-datasets/operations/BBM5LULHPY57D26QENGBFZEA'}\n","Grid 48\n","curr_year 2017\n","Saving data for Hugli 2017\n","Task Started {'state': 'READY', 'description': 'Hugli_48_2017', 'priority': 100, 'creation_timestamp_ms': 1762653202094, 'update_timestamp_ms': 1762653202094, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'OTJUT7NPE4XH3B7X227CQC2E', 'name': 'projects/ext-datasets/operations/OTJUT7NPE4XH3B7X227CQC2E'}\n","Grid 49\n","curr_year 2017\n","Saving data for Hugli 2017\n","Task Started {'state': 'READY', 'description': 'Hugli_49_2017', 'priority': 100, 'creation_timestamp_ms': 1762653207724, 'update_timestamp_ms': 1762653207724, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'EJCTVJWVA35JNT7WY7MS2PEZ', 'name': 'projects/ext-datasets/operations/EJCTVJWVA35JNT7WY7MS2PEZ'}\n","Grid 50\n","curr_year 2017\n","Saving data for Hugli 2017\n","Task Started {'state': 'READY', 'description': 'Hugli_50_2017', 'priority': 100, 'creation_timestamp_ms': 1762653215082, 'update_timestamp_ms': 1762653215082, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '5Q476RLEGWQK22ZGZJVBQNIJ', 'name': 'projects/ext-datasets/operations/5Q476RLEGWQK22ZGZJVBQNIJ'}\n","Grid 51\n","curr_year 2017\n","Saving data for Hugli 2017\n","Task Started {'state': 'READY', 'description': 'Hugli_51_2017', 'priority': 100, 'creation_timestamp_ms': 1762653222507, 'update_timestamp_ms': 1762653222507, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'FEX2WGIRLJCE7IBH74CDGLSO', 'name': 'projects/ext-datasets/operations/FEX2WGIRLJCE7IBH74CDGLSO'}\n","Grid 52\n","curr_year 2017\n","Saving data for Hugli 2017\n","Task Started {'state': 'READY', 'description': 'Hugli_52_2017', 'priority': 100, 'creation_timestamp_ms': 1762653231333, 'update_timestamp_ms': 1762653231333, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'AVYNP6SEDV26DVLT6JEPVBQF', 'name': 'projects/ext-datasets/operations/AVYNP6SEDV26DVLT6JEPVBQF'}\n","Grid 53\n","curr_year 2017\n","Saving data for Hugli 2017\n","Task Started {'state': 'READY', 'description': 'Hugli_53_2017', 'priority': 100, 'creation_timestamp_ms': 1762653239636, 'update_timestamp_ms': 1762653239636, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'ZEQM4XXGVBBLIRHWMUBSV6Q5', 'name': 'projects/ext-datasets/operations/ZEQM4XXGVBBLIRHWMUBSV6Q5'}\n","Grid 54\n","curr_year 2017\n","Saving data for Hugli 2017\n","Task Started {'state': 'READY', 'description': 'Hugli_54_2017', 'priority': 100, 'creation_timestamp_ms': 1762653243926, 'update_timestamp_ms': 1762653243926, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'YHQUJTEO4DJPKTFAOIR4VWXA', 'name': 'projects/ext-datasets/operations/YHQUJTEO4DJPKTFAOIR4VWXA'}\n","3697.579036951065\n","Year 2017, District 18: Kolkata, grids: 6\n","Grid 0\n","curr_year 2017\n","Saving data for Kolkata 2017\n","Task Started {'state': 'READY', 'description': 'Kolkata_0_2017', 'priority': 100, 'creation_timestamp_ms': 1762653255739, 'update_timestamp_ms': 1762653255739, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'UXUXC4D6QFYHE4VSN3ZUDSNP', 'name': 'projects/ext-datasets/operations/UXUXC4D6QFYHE4VSN3ZUDSNP'}\n","Grid 1\n","curr_year 2017\n","Saving data for Kolkata 2017\n","Task Started {'state': 'READY', 'description': 'Kolkata_1_2017', 'priority': 100, 'creation_timestamp_ms': 1762653263107, 'update_timestamp_ms': 1762653263107, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'X4ISR3KUCH25N42LTY2QEIMB', 'name': 'projects/ext-datasets/operations/X4ISR3KUCH25N42LTY2QEIMB'}\n","Grid 2\n","curr_year 2017\n","Saving data for Kolkata 2017\n","Task Started {'state': 'READY', 'description': 'Kolkata_2_2017', 'priority': 100, 'creation_timestamp_ms': 1762653270940, 'update_timestamp_ms': 1762653270940, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'JIXZH7CTRXOXPECWCUEA25CB', 'name': 'projects/ext-datasets/operations/JIXZH7CTRXOXPECWCUEA25CB'}\n","Grid 3\n","curr_year 2017\n","Saving data for Kolkata 2017\n","Task Started {'state': 'READY', 'description': 'Kolkata_3_2017', 'priority': 100, 'creation_timestamp_ms': 1762653278573, 'update_timestamp_ms': 1762653278573, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'RIHCWKL5W7PAS2F5ZERX2FLQ', 'name': 'projects/ext-datasets/operations/RIHCWKL5W7PAS2F5ZERX2FLQ'}\n","Grid 4\n","curr_year 2017\n","Saving data for Kolkata 2017\n","Task Started {'state': 'READY', 'description': 'Kolkata_4_2017', 'priority': 100, 'creation_timestamp_ms': 1762653285117, 'update_timestamp_ms': 1762653285117, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '2MO4V3JQ5U5ZKUVWZWKVOOLC', 'name': 'projects/ext-datasets/operations/2MO4V3JQ5U5ZKUVWZWKVOOLC'}\n","Grid 5\n","curr_year 2017\n","Saving data for Kolkata 2017\n","Task Started {'state': 'READY', 'description': 'Kolkata_5_2017', 'priority': 100, 'creation_timestamp_ms': 1762653292659, 'update_timestamp_ms': 1762653292659, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'MEWVKX5M5UWFLXBD4N3KSMUS', 'name': 'projects/ext-datasets/operations/MEWVKX5M5UWFLXBD4N3KSMUS'}\n","3746.3626384735107\n","Year 2017, District 19: Maldah, grids: 61\n","Grid 0\n","curr_year 2017\n","Saving data for Maldah 2017\n","Task Started {'state': 'READY', 'description': 'Maldah_0_2017', 'priority': 100, 'creation_timestamp_ms': 1762653307026, 'update_timestamp_ms': 1762653307026, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'QAPIPPIW66JXE4SHXRYGGATA', 'name': 'projects/ext-datasets/operations/QAPIPPIW66JXE4SHXRYGGATA'}\n","Grid 1\n","curr_year 2017\n","Saving data for Maldah 2017\n","Task Started {'state': 'READY', 'description': 'Maldah_1_2017', 'priority': 100, 'creation_timestamp_ms': 1762653314549, 'update_timestamp_ms': 1762653314549, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'II3RFO4GMMJQ7VVIITOZTRQ6', 'name': 'projects/ext-datasets/operations/II3RFO4GMMJQ7VVIITOZTRQ6'}\n","Grid 2\n","curr_year 2017\n","Saving data for Maldah 2017\n","Task Started {'state': 'READY', 'description': 'Maldah_2_2017', 'priority': 100, 'creation_timestamp_ms': 1762653322843, 'update_timestamp_ms': 1762653322843, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'GQPNIDVKNVFTOL76HN3PWZA5', 'name': 'projects/ext-datasets/operations/GQPNIDVKNVFTOL76HN3PWZA5'}\n","Grid 3\n","curr_year 2017\n","Saving data for Maldah 2017\n","Task Started {'state': 'READY', 'description': 'Maldah_3_2017', 'priority': 100, 'creation_timestamp_ms': 1762653330638, 'update_timestamp_ms': 1762653330638, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'MQG4FRBMMJL5FE6AU3Y3M35W', 'name': 'projects/ext-datasets/operations/MQG4FRBMMJL5FE6AU3Y3M35W'}\n","Grid 4\n","curr_year 2017\n","Saving data for Maldah 2017\n","Task Started {'state': 'READY', 'description': 'Maldah_4_2017', 'priority': 100, 'creation_timestamp_ms': 1762653337061, 'update_timestamp_ms': 1762653337061, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'RBIFNYSNTWMQIVHH7LF4RU5H', 'name': 'projects/ext-datasets/operations/RBIFNYSNTWMQIVHH7LF4RU5H'}\n","Grid 5\n","curr_year 2017\n","Saving data for Maldah 2017\n","Task Started {'state': 'READY', 'description': 'Maldah_5_2017', 'priority': 100, 'creation_timestamp_ms': 1762653344098, 'update_timestamp_ms': 1762653344098, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'XLYW2F2V5EDDC4WTNIZ7JC6Q', 'name': 'projects/ext-datasets/operations/XLYW2F2V5EDDC4WTNIZ7JC6Q'}\n","Grid 6\n","curr_year 2017\n","Saving data for Maldah 2017\n","Task Started {'state': 'READY', 'description': 'Maldah_6_2017', 'priority': 100, 'creation_timestamp_ms': 1762653352420, 'update_timestamp_ms': 1762653352420, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'EUQV5UNADPFXYD65CQ64UN56', 'name': 'projects/ext-datasets/operations/EUQV5UNADPFXYD65CQ64UN56'}\n","Grid 7\n","curr_year 2017\n","Saving data for Maldah 2017\n","Task Started {'state': 'READY', 'description': 'Maldah_7_2017', 'priority': 100, 'creation_timestamp_ms': 1762653360453, 'update_timestamp_ms': 1762653360453, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'CYJQMBWOLJUOWMBIX3IGGO4V', 'name': 'projects/ext-datasets/operations/CYJQMBWOLJUOWMBIX3IGGO4V'}\n","Grid 8\n","curr_year 2017\n","Saving data for Maldah 2017\n","Task Started {'state': 'READY', 'description': 'Maldah_8_2017', 'priority': 100, 'creation_timestamp_ms': 1762653369302, 'update_timestamp_ms': 1762653369302, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'HJXY66HWTEPRUWXHBVHCKHSU', 'name': 'projects/ext-datasets/operations/HJXY66HWTEPRUWXHBVHCKHSU'}\n","Grid 9\n","curr_year 2017\n","Saving data for Maldah 2017\n","Task Started {'state': 'READY', 'description': 'Maldah_9_2017', 'priority': 100, 'creation_timestamp_ms': 1762653376196, 'update_timestamp_ms': 1762653376196, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'KRCQLFABFHAXYCA27UAOMGQ3', 'name': 'projects/ext-datasets/operations/KRCQLFABFHAXYCA27UAOMGQ3'}\n","Grid 10\n","curr_year 2017\n","Saving data for Maldah 2017\n","Task Started {'state': 'READY', 'description': 'Maldah_10_2017', 'priority': 100, 'creation_timestamp_ms': 1762653383697, 'update_timestamp_ms': 1762653383697, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '3FDGAB5FBCMXHBHYWDDZI62T', 'name': 'projects/ext-datasets/operations/3FDGAB5FBCMXHBHYWDDZI62T'}\n","Grid 11\n","curr_year 2017\n","Saving data for Maldah 2017\n","Task Started {'state': 'READY', 'description': 'Maldah_11_2017', 'priority': 100, 'creation_timestamp_ms': 1762653391609, 'update_timestamp_ms': 1762653391609, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'PTT5B6F4TFKMLHHGA5KGVSHQ', 'name': 'projects/ext-datasets/operations/PTT5B6F4TFKMLHHGA5KGVSHQ'}\n","Grid 12\n","curr_year 2017\n","Saving data for Maldah 2017\n","Task Started {'state': 'READY', 'description': 'Maldah_12_2017', 'priority': 100, 'creation_timestamp_ms': 1762653401352, 'update_timestamp_ms': 1762653401352, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'FWLOBKFWETXJ27PFYAYMBUI5', 'name': 'projects/ext-datasets/operations/FWLOBKFWETXJ27PFYAYMBUI5'}\n","Grid 13\n","curr_year 2017\n","Saving data for Maldah 2017\n","Task Started {'state': 'READY', 'description': 'Maldah_13_2017', 'priority': 100, 'creation_timestamp_ms': 1762653419468, 'update_timestamp_ms': 1762653419468, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '44LAF56HFWQPWWXW5CWUJ7IP', 'name': 'projects/ext-datasets/operations/44LAF56HFWQPWWXW5CWUJ7IP'}\n","Grid 14\n","curr_year 2017\n","Saving data for Maldah 2017\n","Task Started {'state': 'READY', 'description': 'Maldah_14_2017', 'priority': 100, 'creation_timestamp_ms': 1762653427261, 'update_timestamp_ms': 1762653427261, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'SZR5YDJO2YUZK3W55ACIBWTW', 'name': 'projects/ext-datasets/operations/SZR5YDJO2YUZK3W55ACIBWTW'}\n","Grid 15\n","curr_year 2017\n","Saving data for Maldah 2017\n","Task Started {'state': 'READY', 'description': 'Maldah_15_2017', 'priority': 100, 'creation_timestamp_ms': 1762653435984, 'update_timestamp_ms': 1762653435984, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'DL5MAMXB4L3LLQXQ3HIHD6AI', 'name': 'projects/ext-datasets/operations/DL5MAMXB4L3LLQXQ3HIHD6AI'}\n","Grid 16\n","curr_year 2017\n","Saving data for Maldah 2017\n","Task Started {'state': 'READY', 'description': 'Maldah_16_2017', 'priority': 100, 'creation_timestamp_ms': 1762653443583, 'update_timestamp_ms': 1762653443583, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'W4UMT7X5ZJ5L7OQXPK6FB4SD', 'name': 'projects/ext-datasets/operations/W4UMT7X5ZJ5L7OQXPK6FB4SD'}\n","Grid 17\n","curr_year 2017\n","Saving data for Maldah 2017\n","Task Started {'state': 'READY', 'description': 'Maldah_17_2017', 'priority': 100, 'creation_timestamp_ms': 1762653451043, 'update_timestamp_ms': 1762653451043, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'DTPOZN2ZC5H65D7RYPIXZWH5', 'name': 'projects/ext-datasets/operations/DTPOZN2ZC5H65D7RYPIXZWH5'}\n","Grid 18\n","curr_year 2017\n","Saving data for Maldah 2017\n","Task Started {'state': 'READY', 'description': 'Maldah_18_2017', 'priority': 100, 'creation_timestamp_ms': 1762653458890, 'update_timestamp_ms': 1762653458890, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'FVB5AVADD3JEATFLCFBBRKBT', 'name': 'projects/ext-datasets/operations/FVB5AVADD3JEATFLCFBBRKBT'}\n","Grid 19\n","curr_year 2017\n","Saving data for Maldah 2017\n","Task Started {'state': 'READY', 'description': 'Maldah_19_2017', 'priority': 100, 'creation_timestamp_ms': 1762653466409, 'update_timestamp_ms': 1762653466409, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'WHDQ3GKZGMJS5MNJ6Y3H2JO3', 'name': 'projects/ext-datasets/operations/WHDQ3GKZGMJS5MNJ6Y3H2JO3'}\n","Grid 20\n","curr_year 2017\n","Saving data for Maldah 2017\n","Task Started {'state': 'READY', 'description': 'Maldah_20_2017', 'priority': 100, 'creation_timestamp_ms': 1762653474177, 'update_timestamp_ms': 1762653474177, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'NXGV26SJ2FZHQVGAL3QG5PDJ', 'name': 'projects/ext-datasets/operations/NXGV26SJ2FZHQVGAL3QG5PDJ'}\n","Grid 21\n","curr_year 2017\n","Saving data for Maldah 2017\n","Task Started {'state': 'READY', 'description': 'Maldah_21_2017', 'priority': 100, 'creation_timestamp_ms': 1762653481511, 'update_timestamp_ms': 1762653481511, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'QC4ETEY5YN4JWEJEZQH4NFLS', 'name': 'projects/ext-datasets/operations/QC4ETEY5YN4JWEJEZQH4NFLS'}\n","Grid 22\n","curr_year 2017\n","Saving data for Maldah 2017\n","Task Started {'state': 'READY', 'description': 'Maldah_22_2017', 'priority': 100, 'creation_timestamp_ms': 1762653488560, 'update_timestamp_ms': 1762653488560, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'XKGFAZTVNOV7EY4OJRVIDZZV', 'name': 'projects/ext-datasets/operations/XKGFAZTVNOV7EY4OJRVIDZZV'}\n","Grid 23\n","curr_year 2017\n","Saving data for Maldah 2017\n","Task Started {'state': 'READY', 'description': 'Maldah_23_2017', 'priority': 100, 'creation_timestamp_ms': 1762653494949, 'update_timestamp_ms': 1762653494949, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'JBWZZDVCJHEQDKTYUGPVH24N', 'name': 'projects/ext-datasets/operations/JBWZZDVCJHEQDKTYUGPVH24N'}\n","Grid 24\n","curr_year 2017\n","Saving data for Maldah 2017\n","Task Started {'state': 'READY', 'description': 'Maldah_24_2017', 'priority': 100, 'creation_timestamp_ms': 1762653501686, 'update_timestamp_ms': 1762653501686, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'FZ27X6HFBSZWXMWLZ7W6PZSH', 'name': 'projects/ext-datasets/operations/FZ27X6HFBSZWXMWLZ7W6PZSH'}\n","Grid 25\n","curr_year 2017\n","Saving data for Maldah 2017\n","Task Started {'state': 'READY', 'description': 'Maldah_25_2017', 'priority': 100, 'creation_timestamp_ms': 1762653509420, 'update_timestamp_ms': 1762653509420, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'QAMZUMFTIL6QO2CGJVMRXEDX', 'name': 'projects/ext-datasets/operations/QAMZUMFTIL6QO2CGJVMRXEDX'}\n","Grid 26\n","curr_year 2017\n","Saving data for Maldah 2017\n","Task Started {'state': 'READY', 'description': 'Maldah_26_2017', 'priority': 100, 'creation_timestamp_ms': 1762653515776, 'update_timestamp_ms': 1762653515776, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'MAUX2TR736ZX4DRGTLLNIP7Y', 'name': 'projects/ext-datasets/operations/MAUX2TR736ZX4DRGTLLNIP7Y'}\n","Grid 27\n","curr_year 2017\n","Saving data for Maldah 2017\n","Task Started {'state': 'READY', 'description': 'Maldah_27_2017', 'priority': 100, 'creation_timestamp_ms': 1762653522065, 'update_timestamp_ms': 1762653522065, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'QDPDGUGHELDOTRPE5CQP4XZN', 'name': 'projects/ext-datasets/operations/QDPDGUGHELDOTRPE5CQP4XZN'}\n","Grid 28\n","curr_year 2017\n","Saving data for Maldah 2017\n","Task Started {'state': 'READY', 'description': 'Maldah_28_2017', 'priority': 100, 'creation_timestamp_ms': 1762653530057, 'update_timestamp_ms': 1762653530057, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'WZSXJF2ZQZUU4RODFFUFMM75', 'name': 'projects/ext-datasets/operations/WZSXJF2ZQZUU4RODFFUFMM75'}\n","Grid 29\n","curr_year 2017\n","Saving data for Maldah 2017\n","Task Started {'state': 'READY', 'description': 'Maldah_29_2017', 'priority': 100, 'creation_timestamp_ms': 1762653537639, 'update_timestamp_ms': 1762653537639, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'KZPNMLXGNND5IJKGKCJD6TOP', 'name': 'projects/ext-datasets/operations/KZPNMLXGNND5IJKGKCJD6TOP'}\n","Grid 30\n","curr_year 2017\n","Saving data for Maldah 2017\n","Task Started {'state': 'READY', 'description': 'Maldah_30_2017', 'priority': 100, 'creation_timestamp_ms': 1762653544926, 'update_timestamp_ms': 1762653544926, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '7DC5KUBNX2FF3SMR72W34ZHG', 'name': 'projects/ext-datasets/operations/7DC5KUBNX2FF3SMR72W34ZHG'}\n","Grid 31\n","curr_year 2017\n","Saving data for Maldah 2017\n","Task Started {'state': 'READY', 'description': 'Maldah_31_2017', 'priority': 100, 'creation_timestamp_ms': 1762653552527, 'update_timestamp_ms': 1762653552527, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'BLD33OJTOWGHCXTGVZXZIO2P', 'name': 'projects/ext-datasets/operations/BLD33OJTOWGHCXTGVZXZIO2P'}\n","Grid 32\n","curr_year 2017\n","Saving data for Maldah 2017\n","Task Started {'state': 'READY', 'description': 'Maldah_32_2017', 'priority': 100, 'creation_timestamp_ms': 1762653560390, 'update_timestamp_ms': 1762653560390, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'B252XMEYH5O2ZA7L4SJWHFPX', 'name': 'projects/ext-datasets/operations/B252XMEYH5O2ZA7L4SJWHFPX'}\n","Grid 33\n","curr_year 2017\n","Saving data for Maldah 2017\n","Task Started {'state': 'READY', 'description': 'Maldah_33_2017', 'priority': 100, 'creation_timestamp_ms': 1762653563974, 'update_timestamp_ms': 1762653563974, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'L377EDJ5RJO5JIGX32CUBHWC', 'name': 'projects/ext-datasets/operations/L377EDJ5RJO5JIGX32CUBHWC'}\n","Grid 34\n","curr_year 2017\n","Saving data for Maldah 2017\n","Task Started {'state': 'READY', 'description': 'Maldah_34_2017', 'priority': 100, 'creation_timestamp_ms': 1762653571157, 'update_timestamp_ms': 1762653571157, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'VXAL2ERHA3YZGYJAJI6FJM4L', 'name': 'projects/ext-datasets/operations/VXAL2ERHA3YZGYJAJI6FJM4L'}\n","Grid 35\n","curr_year 2017\n","Saving data for Maldah 2017\n","Task Started {'state': 'READY', 'description': 'Maldah_35_2017', 'priority': 100, 'creation_timestamp_ms': 1762653579961, 'update_timestamp_ms': 1762653579961, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'BMP3FFN72TNUTT4J6Q7SRAPN', 'name': 'projects/ext-datasets/operations/BMP3FFN72TNUTT4J6Q7SRAPN'}\n","Grid 36\n","curr_year 2017\n","Saving data for Maldah 2017\n","Task Started {'state': 'READY', 'description': 'Maldah_36_2017', 'priority': 100, 'creation_timestamp_ms': 1762653586557, 'update_timestamp_ms': 1762653586557, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'MR2ZM6CM5POJM2LCSE7NGTSY', 'name': 'projects/ext-datasets/operations/MR2ZM6CM5POJM2LCSE7NGTSY'}\n","Grid 37\n","curr_year 2017\n","Saving data for Maldah 2017\n","Task Started {'state': 'READY', 'description': 'Maldah_37_2017', 'priority': 100, 'creation_timestamp_ms': 1762653595043, 'update_timestamp_ms': 1762653595043, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'XZEAJ3NM2CDE5MNBAV5RXP7X', 'name': 'projects/ext-datasets/operations/XZEAJ3NM2CDE5MNBAV5RXP7X'}\n","Grid 38\n","curr_year 2017\n","Saving data for Maldah 2017\n","Task Started {'state': 'READY', 'description': 'Maldah_38_2017', 'priority': 100, 'creation_timestamp_ms': 1762653603189, 'update_timestamp_ms': 1762653603189, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'SVQGTVZYFMJX56OO4LUGU6FZ', 'name': 'projects/ext-datasets/operations/SVQGTVZYFMJX56OO4LUGU6FZ'}\n","Grid 39\n","curr_year 2017\n","Saving data for Maldah 2017\n","Task Started {'state': 'READY', 'description': 'Maldah_39_2017', 'priority': 100, 'creation_timestamp_ms': 1762653610089, 'update_timestamp_ms': 1762653610089, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '6R77UCAJUFCC3FTNEIWOV5KU', 'name': 'projects/ext-datasets/operations/6R77UCAJUFCC3FTNEIWOV5KU'}\n","Grid 40\n","curr_year 2017\n","Saving data for Maldah 2017\n","Task Started {'state': 'READY', 'description': 'Maldah_40_2017', 'priority': 100, 'creation_timestamp_ms': 1762653616912, 'update_timestamp_ms': 1762653616912, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'AHGO4Z6KJD2MADBE4HJRZXLO', 'name': 'projects/ext-datasets/operations/AHGO4Z6KJD2MADBE4HJRZXLO'}\n","Grid 41\n","curr_year 2017\n","Saving data for Maldah 2017\n","Task Started {'state': 'READY', 'description': 'Maldah_41_2017', 'priority': 100, 'creation_timestamp_ms': 1762653624910, 'update_timestamp_ms': 1762653624910, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '4L6DLRUNVBFADPTVT4RWRAY3', 'name': 'projects/ext-datasets/operations/4L6DLRUNVBFADPTVT4RWRAY3'}\n","Grid 42\n","curr_year 2017\n","Saving data for Maldah 2017\n","Task Started {'state': 'READY', 'description': 'Maldah_42_2017', 'priority': 100, 'creation_timestamp_ms': 1762653631524, 'update_timestamp_ms': 1762653631524, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'QGWJG63I2NHNLEHSDS6BNXA6', 'name': 'projects/ext-datasets/operations/QGWJG63I2NHNLEHSDS6BNXA6'}\n","Grid 43\n","curr_year 2017\n","Saving data for Maldah 2017\n","Task Started {'state': 'READY', 'description': 'Maldah_43_2017', 'priority': 100, 'creation_timestamp_ms': 1762653639518, 'update_timestamp_ms': 1762653639518, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'XE3NBLV25Z2I4Q3SQCP4MGAH', 'name': 'projects/ext-datasets/operations/XE3NBLV25Z2I4Q3SQCP4MGAH'}\n","Grid 44\n","curr_year 2017\n","Saving data for Maldah 2017\n","Task Started {'state': 'READY', 'description': 'Maldah_44_2017', 'priority': 100, 'creation_timestamp_ms': 1762653646429, 'update_timestamp_ms': 1762653646429, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'HRAXBS7AIGEPBX4HP3ROTLVN', 'name': 'projects/ext-datasets/operations/HRAXBS7AIGEPBX4HP3ROTLVN'}\n","Grid 45\n","curr_year 2017\n","Saving data for Maldah 2017\n","Task Started {'state': 'READY', 'description': 'Maldah_45_2017', 'priority': 100, 'creation_timestamp_ms': 1762653654205, 'update_timestamp_ms': 1762653654205, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '5NA5YFUVXGDNTNRCEZG7DIK2', 'name': 'projects/ext-datasets/operations/5NA5YFUVXGDNTNRCEZG7DIK2'}\n","Grid 46\n","curr_year 2017\n","Saving data for Maldah 2017\n","Task Started {'state': 'READY', 'description': 'Maldah_46_2017', 'priority': 100, 'creation_timestamp_ms': 1762653660439, 'update_timestamp_ms': 1762653660439, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'CWFOGQOWCFIAKBBNAIVK5WM5', 'name': 'projects/ext-datasets/operations/CWFOGQOWCFIAKBBNAIVK5WM5'}\n","Grid 47\n","curr_year 2017\n","Saving data for Maldah 2017\n","Task Started {'state': 'READY', 'description': 'Maldah_47_2017', 'priority': 100, 'creation_timestamp_ms': 1762653669479, 'update_timestamp_ms': 1762653669479, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '2H22QVZXDN3LNHSUAC4UJCQ4', 'name': 'projects/ext-datasets/operations/2H22QVZXDN3LNHSUAC4UJCQ4'}\n","Grid 48\n","curr_year 2017\n","Saving data for Maldah 2017\n","Task Started {'state': 'READY', 'description': 'Maldah_48_2017', 'priority': 100, 'creation_timestamp_ms': 1762653673328, 'update_timestamp_ms': 1762653673328, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'EMSW7CJWFFWQ6APGYTPECA2P', 'name': 'projects/ext-datasets/operations/EMSW7CJWFFWQ6APGYTPECA2P'}\n","Grid 49\n","curr_year 2017\n","Saving data for Maldah 2017\n","Task Started {'state': 'READY', 'description': 'Maldah_49_2017', 'priority': 100, 'creation_timestamp_ms': 1762653680761, 'update_timestamp_ms': 1762653680761, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'KB6GXPQTM66LGRZUEVNBYHQY', 'name': 'projects/ext-datasets/operations/KB6GXPQTM66LGRZUEVNBYHQY'}\n","Grid 50\n","curr_year 2017\n","Saving data for Maldah 2017\n","Task Started {'state': 'READY', 'description': 'Maldah_50_2017', 'priority': 100, 'creation_timestamp_ms': 1762653687933, 'update_timestamp_ms': 1762653687933, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'BIYFLVYMNTLUEBSHJVGBNJ7M', 'name': 'projects/ext-datasets/operations/BIYFLVYMNTLUEBSHJVGBNJ7M'}\n","Grid 51\n","curr_year 2017\n","Saving data for Maldah 2017\n","Task Started {'state': 'READY', 'description': 'Maldah_51_2017', 'priority': 100, 'creation_timestamp_ms': 1762653696412, 'update_timestamp_ms': 1762653696412, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'OHND56M62MWDUBE7JZMQFMHZ', 'name': 'projects/ext-datasets/operations/OHND56M62MWDUBE7JZMQFMHZ'}\n","Grid 52\n","curr_year 2017\n","Saving data for Maldah 2017\n","Task Started {'state': 'READY', 'description': 'Maldah_52_2017', 'priority': 100, 'creation_timestamp_ms': 1762653705129, 'update_timestamp_ms': 1762653705129, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'AEPLV4U3722KMODMFNVOHHK6', 'name': 'projects/ext-datasets/operations/AEPLV4U3722KMODMFNVOHHK6'}\n","Grid 53\n","curr_year 2017\n","Saving data for Maldah 2017\n","Task Started {'state': 'READY', 'description': 'Maldah_53_2017', 'priority': 100, 'creation_timestamp_ms': 1762653713348, 'update_timestamp_ms': 1762653713348, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'PPCO5ZYVPYU3U4NPRNSBPLJL', 'name': 'projects/ext-datasets/operations/PPCO5ZYVPYU3U4NPRNSBPLJL'}\n","Grid 54\n","curr_year 2017\n","Saving data for Maldah 2017\n","Task Started {'state': 'READY', 'description': 'Maldah_54_2017', 'priority': 100, 'creation_timestamp_ms': 1762653721065, 'update_timestamp_ms': 1762653721065, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'DGHL73DRSIR2MVY4CXWZK4YO', 'name': 'projects/ext-datasets/operations/DGHL73DRSIR2MVY4CXWZK4YO'}\n","Grid 55\n","curr_year 2017\n","Saving data for Maldah 2017\n","Task Started {'state': 'READY', 'description': 'Maldah_55_2017', 'priority': 100, 'creation_timestamp_ms': 1762653728737, 'update_timestamp_ms': 1762653728737, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'DUECP6FOWWNQEQIQIFVN33DN', 'name': 'projects/ext-datasets/operations/DUECP6FOWWNQEQIQIFVN33DN'}\n","Grid 56\n","curr_year 2017\n","Saving data for Maldah 2017\n","Task Started {'state': 'READY', 'description': 'Maldah_56_2017', 'priority': 100, 'creation_timestamp_ms': 1762653736532, 'update_timestamp_ms': 1762653736532, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '4I6S5YXZZT7EFL2DFDQ5AGF4', 'name': 'projects/ext-datasets/operations/4I6S5YXZZT7EFL2DFDQ5AGF4'}\n","Grid 57\n","curr_year 2017\n","Saving data for Maldah 2017\n","Task Started {'state': 'READY', 'description': 'Maldah_57_2017', 'priority': 100, 'creation_timestamp_ms': 1762653744879, 'update_timestamp_ms': 1762653744879, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'TIQWF4ZWIORMW7BTWRGXPOBA', 'name': 'projects/ext-datasets/operations/TIQWF4ZWIORMW7BTWRGXPOBA'}\n","Grid 58\n","curr_year 2017\n","Saving data for Maldah 2017\n","Task Started {'state': 'READY', 'description': 'Maldah_58_2017', 'priority': 100, 'creation_timestamp_ms': 1762653750799, 'update_timestamp_ms': 1762653750799, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'GNY6NKPSF53UNE6JEJEFLKQE', 'name': 'projects/ext-datasets/operations/GNY6NKPSF53UNE6JEJEFLKQE'}\n","Grid 59\n","curr_year 2017\n","Saving data for Maldah 2017\n","Task Started {'state': 'READY', 'description': 'Maldah_59_2017', 'priority': 100, 'creation_timestamp_ms': 1762653757995, 'update_timestamp_ms': 1762653757995, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'BRH4LJTFLB5NIFYX23Z2D24K', 'name': 'projects/ext-datasets/operations/BRH4LJTFLB5NIFYX23Z2D24K'}\n","Grid 60\n","curr_year 2017\n","Saving data for Maldah 2017\n","Task Started {'state': 'READY', 'description': 'Maldah_60_2017', 'priority': 100, 'creation_timestamp_ms': 1762653763814, 'update_timestamp_ms': 1762653763814, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'DCUJBEOPU66DWIDE63GS7TFK', 'name': 'projects/ext-datasets/operations/DCUJBEOPU66DWIDE63GS7TFK'}\n","4217.510969877243\n","Year 2017, District 20: Murshidabad, grids: 87\n","Grid 0\n","curr_year 2017\n","Saving data for Murshidabad 2017\n","Task Started {'state': 'READY', 'description': 'Murshidabad_0_2017', 'priority': 100, 'creation_timestamp_ms': 1762653777841, 'update_timestamp_ms': 1762653777841, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'NX2VXZOCFQGCIMGOUVTSJTF4', 'name': 'projects/ext-datasets/operations/NX2VXZOCFQGCIMGOUVTSJTF4'}\n","Grid 1\n","curr_year 2017\n","Saving data for Murshidabad 2017\n","Task Started {'state': 'READY', 'description': 'Murshidabad_1_2017', 'priority': 100, 'creation_timestamp_ms': 1762653784734, 'update_timestamp_ms': 1762653784734, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'ZUAMH7MMA6G4MHBCMJKVFJ32', 'name': 'projects/ext-datasets/operations/ZUAMH7MMA6G4MHBCMJKVFJ32'}\n","Grid 2\n","curr_year 2017\n","Saving data for Murshidabad 2017\n","Task Started {'state': 'READY', 'description': 'Murshidabad_2_2017', 'priority': 100, 'creation_timestamp_ms': 1762653789468, 'update_timestamp_ms': 1762653789468, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'EWKFB3TZCYETDAPV6JJOTKSO', 'name': 'projects/ext-datasets/operations/EWKFB3TZCYETDAPV6JJOTKSO'}\n","Grid 3\n","curr_year 2017\n","Saving data for Murshidabad 2017\n","Task Started {'state': 'READY', 'description': 'Murshidabad_3_2017', 'priority': 100, 'creation_timestamp_ms': 1762653797795, 'update_timestamp_ms': 1762653797795, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'RNSUJECPKWVXFW5ZIMHJC3B7', 'name': 'projects/ext-datasets/operations/RNSUJECPKWVXFW5ZIMHJC3B7'}\n","Grid 4\n","curr_year 2017\n","Saving data for Murshidabad 2017\n","Task Started {'state': 'READY', 'description': 'Murshidabad_4_2017', 'priority': 100, 'creation_timestamp_ms': 1762653805593, 'update_timestamp_ms': 1762653805593, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'YRKBCIIRDJNL2AA735MXNV3J', 'name': 'projects/ext-datasets/operations/YRKBCIIRDJNL2AA735MXNV3J'}\n","Grid 5\n","curr_year 2017\n","Saving data for Murshidabad 2017\n","Task Started {'state': 'READY', 'description': 'Murshidabad_5_2017', 'priority': 100, 'creation_timestamp_ms': 1762653811334, 'update_timestamp_ms': 1762653811334, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'H4KZJ6RHG3I73I2VG3PGQP5Y', 'name': 'projects/ext-datasets/operations/H4KZJ6RHG3I73I2VG3PGQP5Y'}\n","Grid 6\n","curr_year 2017\n","Saving data for Murshidabad 2017\n","Task Started {'state': 'READY', 'description': 'Murshidabad_6_2017', 'priority': 100, 'creation_timestamp_ms': 1762653818350, 'update_timestamp_ms': 1762653818350, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '2AWVITKPMQ4CKI7PX7ISXONF', 'name': 'projects/ext-datasets/operations/2AWVITKPMQ4CKI7PX7ISXONF'}\n","Grid 7\n","curr_year 2017\n","Saving data for Murshidabad 2017\n","Task Started {'state': 'READY', 'description': 'Murshidabad_7_2017', 'priority': 100, 'creation_timestamp_ms': 1762653822840, 'update_timestamp_ms': 1762653822840, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'BZ5VSADHRL67CKQ4NLWJTT2Q', 'name': 'projects/ext-datasets/operations/BZ5VSADHRL67CKQ4NLWJTT2Q'}\n","Grid 8\n","curr_year 2017\n","Saving data for Murshidabad 2017\n","Task Started {'state': 'READY', 'description': 'Murshidabad_8_2017', 'priority': 100, 'creation_timestamp_ms': 1762653830584, 'update_timestamp_ms': 1762653830584, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'HFHFUCAEXLHFHUIP6YE754JP', 'name': 'projects/ext-datasets/operations/HFHFUCAEXLHFHUIP6YE754JP'}\n","Grid 9\n","curr_year 2017\n","Saving data for Murshidabad 2017\n","Task Started {'state': 'READY', 'description': 'Murshidabad_9_2017', 'priority': 100, 'creation_timestamp_ms': 1762653838966, 'update_timestamp_ms': 1762653838966, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '6Y2G4VKF3KTUDO3YYLYMLEID', 'name': 'projects/ext-datasets/operations/6Y2G4VKF3KTUDO3YYLYMLEID'}\n","Grid 10\n","curr_year 2017\n","Saving data for Murshidabad 2017\n","Task Started {'state': 'READY', 'description': 'Murshidabad_10_2017', 'priority': 100, 'creation_timestamp_ms': 1762653846040, 'update_timestamp_ms': 1762653846040, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '6NSOWXNHMOWNSV6NCTWXP5CB', 'name': 'projects/ext-datasets/operations/6NSOWXNHMOWNSV6NCTWXP5CB'}\n","Grid 11\n","curr_year 2017\n","Saving data for Murshidabad 2017\n","Task Started {'state': 'READY', 'description': 'Murshidabad_11_2017', 'priority': 100, 'creation_timestamp_ms': 1762653852887, 'update_timestamp_ms': 1762653852887, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '5FDF4VYLPS5WXUJVDZFCKBYZ', 'name': 'projects/ext-datasets/operations/5FDF4VYLPS5WXUJVDZFCKBYZ'}\n","Grid 12\n","curr_year 2017\n","Saving data for Murshidabad 2017\n","Task Started {'state': 'READY', 'description': 'Murshidabad_12_2017', 'priority': 100, 'creation_timestamp_ms': 1762653860246, 'update_timestamp_ms': 1762653860246, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'BF5RASHVLHR6PFDXNYEGFMCW', 'name': 'projects/ext-datasets/operations/BF5RASHVLHR6PFDXNYEGFMCW'}\n","Grid 13\n","curr_year 2017\n","Saving data for Murshidabad 2017\n","Task Started {'state': 'READY', 'description': 'Murshidabad_13_2017', 'priority': 100, 'creation_timestamp_ms': 1762653866589, 'update_timestamp_ms': 1762653866589, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'E2CKWLS4IVVQ6MBWTFH3C3K4', 'name': 'projects/ext-datasets/operations/E2CKWLS4IVVQ6MBWTFH3C3K4'}\n","Grid 14\n","curr_year 2017\n","Saving data for Murshidabad 2017\n","Task Started {'state': 'READY', 'description': 'Murshidabad_14_2017', 'priority': 100, 'creation_timestamp_ms': 1762653873324, 'update_timestamp_ms': 1762653873324, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '46IQBJ7Q5WF4JA76HEXULARX', 'name': 'projects/ext-datasets/operations/46IQBJ7Q5WF4JA76HEXULARX'}\n","Grid 15\n","curr_year 2017\n","Saving data for Murshidabad 2017\n","Task Started {'state': 'READY', 'description': 'Murshidabad_15_2017', 'priority': 100, 'creation_timestamp_ms': 1762653881176, 'update_timestamp_ms': 1762653881176, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '4NQJKDXO4OVFAK7MXPFEDZH6', 'name': 'projects/ext-datasets/operations/4NQJKDXO4OVFAK7MXPFEDZH6'}\n","Grid 16\n","curr_year 2017\n","Saving data for Murshidabad 2017\n","Task Started {'state': 'READY', 'description': 'Murshidabad_16_2017', 'priority': 100, 'creation_timestamp_ms': 1762653888440, 'update_timestamp_ms': 1762653888440, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'QU43U6BDPA4N35BDT5NJJIXS', 'name': 'projects/ext-datasets/operations/QU43U6BDPA4N35BDT5NJJIXS'}\n","Grid 17\n","curr_year 2017\n","Saving data for Murshidabad 2017\n","Task Started {'state': 'READY', 'description': 'Murshidabad_17_2017', 'priority': 100, 'creation_timestamp_ms': 1762653895140, 'update_timestamp_ms': 1762653895140, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'XKLNC5QV3WSY4CFMWVAEMAIN', 'name': 'projects/ext-datasets/operations/XKLNC5QV3WSY4CFMWVAEMAIN'}\n","Grid 18\n","curr_year 2017\n","Saving data for Murshidabad 2017\n","Task Started {'state': 'READY', 'description': 'Murshidabad_18_2017', 'priority': 100, 'creation_timestamp_ms': 1762653904788, 'update_timestamp_ms': 1762653904788, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'GBJHBSZ2S4ELOUHGGSIUDMG7', 'name': 'projects/ext-datasets/operations/GBJHBSZ2S4ELOUHGGSIUDMG7'}\n","Grid 19\n","curr_year 2017\n","Saving data for Murshidabad 2017\n","Task Started {'state': 'READY', 'description': 'Murshidabad_19_2017', 'priority': 100, 'creation_timestamp_ms': 1762653912798, 'update_timestamp_ms': 1762653912798, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'KKB6K7JJR5NW33DZRDFX3YK4', 'name': 'projects/ext-datasets/operations/KKB6K7JJR5NW33DZRDFX3YK4'}\n","Grid 20\n","curr_year 2017\n","Saving data for Murshidabad 2017\n","Task Started {'state': 'READY', 'description': 'Murshidabad_20_2017', 'priority': 100, 'creation_timestamp_ms': 1762653919534, 'update_timestamp_ms': 1762653919534, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'UJ4UD5WWKANSIE2AWNTGMU4Q', 'name': 'projects/ext-datasets/operations/UJ4UD5WWKANSIE2AWNTGMU4Q'}\n","Grid 21\n","curr_year 2017\n","Saving data for Murshidabad 2017\n","Task Started {'state': 'READY', 'description': 'Murshidabad_21_2017', 'priority': 100, 'creation_timestamp_ms': 1762653925878, 'update_timestamp_ms': 1762653925878, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'ET77UW3Y3THD7NJM52CSYJNO', 'name': 'projects/ext-datasets/operations/ET77UW3Y3THD7NJM52CSYJNO'}\n","Grid 22\n","curr_year 2017\n","Saving data for Murshidabad 2017\n","Task Started {'state': 'READY', 'description': 'Murshidabad_22_2017', 'priority': 100, 'creation_timestamp_ms': 1762653932951, 'update_timestamp_ms': 1762653932951, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '2MRIBK3DGIVLB7LEI3IDII5W', 'name': 'projects/ext-datasets/operations/2MRIBK3DGIVLB7LEI3IDII5W'}\n","Grid 23\n","curr_year 2017\n","Saving data for Murshidabad 2017\n","Task Started {'state': 'READY', 'description': 'Murshidabad_23_2017', 'priority': 100, 'creation_timestamp_ms': 1762653943326, 'update_timestamp_ms': 1762653943326, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'RH42F7CGTX5HHIXR22M3DXW4', 'name': 'projects/ext-datasets/operations/RH42F7CGTX5HHIXR22M3DXW4'}\n","Grid 24\n","curr_year 2017\n","Saving data for Murshidabad 2017\n","Task Started {'state': 'READY', 'description': 'Murshidabad_24_2017', 'priority': 100, 'creation_timestamp_ms': 1762653949452, 'update_timestamp_ms': 1762653949452, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'NBXQWQYYKWZO7REI3E2KFCY2', 'name': 'projects/ext-datasets/operations/NBXQWQYYKWZO7REI3E2KFCY2'}\n","Grid 25\n","curr_year 2017\n","Saving data for Murshidabad 2017\n","Task Started {'state': 'READY', 'description': 'Murshidabad_25_2017', 'priority': 100, 'creation_timestamp_ms': 1762653957880, 'update_timestamp_ms': 1762653957880, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '3HLL5UWJBBN4SNVIXAVSB45J', 'name': 'projects/ext-datasets/operations/3HLL5UWJBBN4SNVIXAVSB45J'}\n","Grid 26\n","curr_year 2017\n","Saving data for Murshidabad 2017\n","Task Started {'state': 'READY', 'description': 'Murshidabad_26_2017', 'priority': 100, 'creation_timestamp_ms': 1762653964804, 'update_timestamp_ms': 1762653964804, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'PPPJZCYRFS74YB2KC72AAF4O', 'name': 'projects/ext-datasets/operations/PPPJZCYRFS74YB2KC72AAF4O'}\n","Grid 27\n","curr_year 2017\n","Saving data for Murshidabad 2017\n","Task Started {'state': 'READY', 'description': 'Murshidabad_27_2017', 'priority': 100, 'creation_timestamp_ms': 1762653971742, 'update_timestamp_ms': 1762653971742, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'KRIKZXHKRXA6A4RF3C5ZZMME', 'name': 'projects/ext-datasets/operations/KRIKZXHKRXA6A4RF3C5ZZMME'}\n","Grid 28\n","curr_year 2017\n","Saving data for Murshidabad 2017\n","Task Started {'state': 'READY', 'description': 'Murshidabad_28_2017', 'priority': 100, 'creation_timestamp_ms': 1762653979906, 'update_timestamp_ms': 1762653979906, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'LBDOREIVQQ735JGAHEMFGRAU', 'name': 'projects/ext-datasets/operations/LBDOREIVQQ735JGAHEMFGRAU'}\n","Grid 29\n","curr_year 2017\n","Saving data for Murshidabad 2017\n","Task Started {'state': 'READY', 'description': 'Murshidabad_29_2017', 'priority': 100, 'creation_timestamp_ms': 1762653988463, 'update_timestamp_ms': 1762653988463, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'SN6G6M2C55NZ352BVJAQ35A4', 'name': 'projects/ext-datasets/operations/SN6G6M2C55NZ352BVJAQ35A4'}\n","Grid 30\n","curr_year 2017\n","Saving data for Murshidabad 2017\n","Task Started {'state': 'READY', 'description': 'Murshidabad_30_2017', 'priority': 100, 'creation_timestamp_ms': 1762653996378, 'update_timestamp_ms': 1762653996378, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'QA5MAZT6HTJH5RQPNADA4BUK', 'name': 'projects/ext-datasets/operations/QA5MAZT6HTJH5RQPNADA4BUK'}\n","Grid 31\n","curr_year 2017\n","Saving data for Murshidabad 2017\n","Task Started {'state': 'READY', 'description': 'Murshidabad_31_2017', 'priority': 100, 'creation_timestamp_ms': 1762654001857, 'update_timestamp_ms': 1762654001857, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'AZ6HHRDQMKOYQHIYLMQNBGKR', 'name': 'projects/ext-datasets/operations/AZ6HHRDQMKOYQHIYLMQNBGKR'}\n","Grid 32\n","curr_year 2017\n","Saving data for Murshidabad 2017\n","Task Started {'state': 'READY', 'description': 'Murshidabad_32_2017', 'priority': 100, 'creation_timestamp_ms': 1762654009445, 'update_timestamp_ms': 1762654009445, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'BEQN5GMLUCYMRSUUHIPODHYB', 'name': 'projects/ext-datasets/operations/BEQN5GMLUCYMRSUUHIPODHYB'}\n","Grid 33\n","curr_year 2017\n","Saving data for Murshidabad 2017\n","Task Started {'state': 'READY', 'description': 'Murshidabad_33_2017', 'priority': 100, 'creation_timestamp_ms': 1762654016924, 'update_timestamp_ms': 1762654016924, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '44HVPDN62JQH7BUOFR2VQ6LN', 'name': 'projects/ext-datasets/operations/44HVPDN62JQH7BUOFR2VQ6LN'}\n","Grid 34\n","curr_year 2017\n","Saving data for Murshidabad 2017\n","Task Started {'state': 'READY', 'description': 'Murshidabad_34_2017', 'priority': 100, 'creation_timestamp_ms': 1762654023800, 'update_timestamp_ms': 1762654023800, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'VWZG7RILSLQ4AJFQRK2S3WSS', 'name': 'projects/ext-datasets/operations/VWZG7RILSLQ4AJFQRK2S3WSS'}\n","Grid 35\n","curr_year 2017\n","Saving data for Murshidabad 2017\n","Task Started {'state': 'READY', 'description': 'Murshidabad_35_2017', 'priority': 100, 'creation_timestamp_ms': 1762654032471, 'update_timestamp_ms': 1762654032471, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'KUBX4FOSYCWCTCI75R7OABNH', 'name': 'projects/ext-datasets/operations/KUBX4FOSYCWCTCI75R7OABNH'}\n","Grid 36\n","curr_year 2017\n","Saving data for Murshidabad 2017\n","Task Started {'state': 'READY', 'description': 'Murshidabad_36_2017', 'priority': 100, 'creation_timestamp_ms': 1762654040811, 'update_timestamp_ms': 1762654040811, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'KB7BCNNPE6GIJW72DTNL4RZQ', 'name': 'projects/ext-datasets/operations/KB7BCNNPE6GIJW72DTNL4RZQ'}\n","Grid 37\n","curr_year 2017\n","Saving data for Murshidabad 2017\n","Task Started {'state': 'READY', 'description': 'Murshidabad_37_2017', 'priority': 100, 'creation_timestamp_ms': 1762654049211, 'update_timestamp_ms': 1762654049211, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'JLPHSI6XVO56J7PKYQQWM2DS', 'name': 'projects/ext-datasets/operations/JLPHSI6XVO56J7PKYQQWM2DS'}\n","Grid 38\n","curr_year 2017\n","Saving data for Murshidabad 2017\n","Task Started {'state': 'READY', 'description': 'Murshidabad_38_2017', 'priority': 100, 'creation_timestamp_ms': 1762654057262, 'update_timestamp_ms': 1762654057262, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'SKLC4T64M27G46OKHYBTJPZE', 'name': 'projects/ext-datasets/operations/SKLC4T64M27G46OKHYBTJPZE'}\n","Grid 39\n","curr_year 2017\n","Saving data for Murshidabad 2017\n","Task Started {'state': 'READY', 'description': 'Murshidabad_39_2017', 'priority': 100, 'creation_timestamp_ms': 1762654066731, 'update_timestamp_ms': 1762654066731, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'FFARSCE33IKE74VD5CXXY7KZ', 'name': 'projects/ext-datasets/operations/FFARSCE33IKE74VD5CXXY7KZ'}\n","Grid 40\n","curr_year 2017\n","Saving data for Murshidabad 2017\n","Task Started {'state': 'READY', 'description': 'Murshidabad_40_2017', 'priority': 100, 'creation_timestamp_ms': 1762654071677, 'update_timestamp_ms': 1762654071677, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'BVJUW6FK3OAJLPY544MSEXKZ', 'name': 'projects/ext-datasets/operations/BVJUW6FK3OAJLPY544MSEXKZ'}\n","Grid 41\n","curr_year 2017\n","Saving data for Murshidabad 2017\n","Task Started {'state': 'READY', 'description': 'Murshidabad_41_2017', 'priority': 100, 'creation_timestamp_ms': 1762654076894, 'update_timestamp_ms': 1762654076894, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'QLFPYEPBTWNWVMFHBKEB7VPJ', 'name': 'projects/ext-datasets/operations/QLFPYEPBTWNWVMFHBKEB7VPJ'}\n","Grid 42\n","curr_year 2017\n","Saving data for Murshidabad 2017\n","Task Started {'state': 'READY', 'description': 'Murshidabad_42_2017', 'priority': 100, 'creation_timestamp_ms': 1762654084155, 'update_timestamp_ms': 1762654084155, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'Z6HSU344JNLLONL4TAWFXB7E', 'name': 'projects/ext-datasets/operations/Z6HSU344JNLLONL4TAWFXB7E'}\n","Grid 43\n","curr_year 2017\n","Saving data for Murshidabad 2017\n","Task Started {'state': 'READY', 'description': 'Murshidabad_43_2017', 'priority': 100, 'creation_timestamp_ms': 1762654096054, 'update_timestamp_ms': 1762654096054, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '4HJ6CZEXP2ZWMHBM57ER3RD7', 'name': 'projects/ext-datasets/operations/4HJ6CZEXP2ZWMHBM57ER3RD7'}\n","Grid 44\n","curr_year 2017\n","Saving data for Murshidabad 2017\n","Task Started {'state': 'READY', 'description': 'Murshidabad_44_2017', 'priority': 100, 'creation_timestamp_ms': 1762654103872, 'update_timestamp_ms': 1762654103872, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'R6HZAE6XRQWNPOQUZTJXLBDY', 'name': 'projects/ext-datasets/operations/R6HZAE6XRQWNPOQUZTJXLBDY'}\n","Grid 45\n","curr_year 2017\n","Saving data for Murshidabad 2017\n","Task Started {'state': 'READY', 'description': 'Murshidabad_45_2017', 'priority': 100, 'creation_timestamp_ms': 1762654108933, 'update_timestamp_ms': 1762654108933, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'SSPWCJS7V3HRF2HPA652YD4T', 'name': 'projects/ext-datasets/operations/SSPWCJS7V3HRF2HPA652YD4T'}\n","Grid 46\n","curr_year 2017\n","Saving data for Murshidabad 2017\n","Task Started {'state': 'READY', 'description': 'Murshidabad_46_2017', 'priority': 100, 'creation_timestamp_ms': 1762654116877, 'update_timestamp_ms': 1762654116877, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'EG2PSYE6HTAWGVIJSPH6GVAH', 'name': 'projects/ext-datasets/operations/EG2PSYE6HTAWGVIJSPH6GVAH'}\n","Grid 47\n","curr_year 2017\n","Saving data for Murshidabad 2017\n","Task Started {'state': 'READY', 'description': 'Murshidabad_47_2017', 'priority': 100, 'creation_timestamp_ms': 1762654123631, 'update_timestamp_ms': 1762654123631, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'VGRICQZE2AKI33AUXFTY7LJL', 'name': 'projects/ext-datasets/operations/VGRICQZE2AKI33AUXFTY7LJL'}\n","Grid 48\n","curr_year 2017\n","Saving data for Murshidabad 2017\n","Task Started {'state': 'READY', 'description': 'Murshidabad_48_2017', 'priority': 100, 'creation_timestamp_ms': 1762654133474, 'update_timestamp_ms': 1762654133474, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '37MGLXZAP4AEDSGNHR4YTFPK', 'name': 'projects/ext-datasets/operations/37MGLXZAP4AEDSGNHR4YTFPK'}\n","Grid 49\n","curr_year 2017\n","Saving data for Murshidabad 2017\n","Task Started {'state': 'READY', 'description': 'Murshidabad_49_2017', 'priority': 100, 'creation_timestamp_ms': 1762654141171, 'update_timestamp_ms': 1762654141171, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'XCWB546AUSPANV7JYRNXDRA6', 'name': 'projects/ext-datasets/operations/XCWB546AUSPANV7JYRNXDRA6'}\n","Grid 50\n","curr_year 2017\n","Saving data for Murshidabad 2017\n","Task Started {'state': 'READY', 'description': 'Murshidabad_50_2017', 'priority': 100, 'creation_timestamp_ms': 1762654145765, 'update_timestamp_ms': 1762654145765, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '2GCUUDHIUD342CLSQHQCOOJE', 'name': 'projects/ext-datasets/operations/2GCUUDHIUD342CLSQHQCOOJE'}\n","Grid 51\n","curr_year 2017\n","Saving data for Murshidabad 2017\n","Task Started {'state': 'READY', 'description': 'Murshidabad_51_2017', 'priority': 100, 'creation_timestamp_ms': 1762654154438, 'update_timestamp_ms': 1762654154438, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'ZEKHWK5DQ62KX4SYDI76W3QV', 'name': 'projects/ext-datasets/operations/ZEKHWK5DQ62KX4SYDI76W3QV'}\n","Grid 52\n","curr_year 2017\n","Saving data for Murshidabad 2017\n","Task Started {'state': 'READY', 'description': 'Murshidabad_52_2017', 'priority': 100, 'creation_timestamp_ms': 1762654160861, 'update_timestamp_ms': 1762654160861, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '4VAWOFVC5P5PRRD6VVTS3H73', 'name': 'projects/ext-datasets/operations/4VAWOFVC5P5PRRD6VVTS3H73'}\n","Grid 53\n","curr_year 2017\n","Saving data for Murshidabad 2017\n","Task Started {'state': 'READY', 'description': 'Murshidabad_53_2017', 'priority': 100, 'creation_timestamp_ms': 1762654168851, 'update_timestamp_ms': 1762654168851, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'LOKEALXX57SWTCFETEBPNSIX', 'name': 'projects/ext-datasets/operations/LOKEALXX57SWTCFETEBPNSIX'}\n","Grid 54\n","curr_year 2017\n","Saving data for Murshidabad 2017\n","Task Started {'state': 'READY', 'description': 'Murshidabad_54_2017', 'priority': 100, 'creation_timestamp_ms': 1762654176454, 'update_timestamp_ms': 1762654176454, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'OGYQ23YWNQI7R2KYVYJ7VL52', 'name': 'projects/ext-datasets/operations/OGYQ23YWNQI7R2KYVYJ7VL52'}\n","Grid 55\n","curr_year 2017\n","Saving data for Murshidabad 2017\n","Task Started {'state': 'READY', 'description': 'Murshidabad_55_2017', 'priority': 100, 'creation_timestamp_ms': 1762654183923, 'update_timestamp_ms': 1762654183923, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'UYJ4Z7K25V4NX7VILLH2UD5Y', 'name': 'projects/ext-datasets/operations/UYJ4Z7K25V4NX7VILLH2UD5Y'}\n","Grid 56\n","curr_year 2017\n","Saving data for Murshidabad 2017\n","Task Started {'state': 'READY', 'description': 'Murshidabad_56_2017', 'priority': 100, 'creation_timestamp_ms': 1762654191408, 'update_timestamp_ms': 1762654191408, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'JOCJYMG3UWIIVPI4A6OB37JJ', 'name': 'projects/ext-datasets/operations/JOCJYMG3UWIIVPI4A6OB37JJ'}\n","Grid 57\n","curr_year 2017\n","Saving data for Murshidabad 2017\n","Task Started {'state': 'READY', 'description': 'Murshidabad_57_2017', 'priority': 100, 'creation_timestamp_ms': 1762654194986, 'update_timestamp_ms': 1762654194986, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'SWCFXQQCMDTVOKHGU2FJNTAQ', 'name': 'projects/ext-datasets/operations/SWCFXQQCMDTVOKHGU2FJNTAQ'}\n","Grid 58\n","curr_year 2017\n","Saving data for Murshidabad 2017\n","Task Started {'state': 'READY', 'description': 'Murshidabad_58_2017', 'priority': 100, 'creation_timestamp_ms': 1762654202860, 'update_timestamp_ms': 1762654202860, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'UVZ5XDC26KR5UIYJPBZRJEHA', 'name': 'projects/ext-datasets/operations/UVZ5XDC26KR5UIYJPBZRJEHA'}\n","Grid 59\n","curr_year 2017\n","Saving data for Murshidabad 2017\n","Task Started {'state': 'READY', 'description': 'Murshidabad_59_2017', 'priority': 100, 'creation_timestamp_ms': 1762654206594, 'update_timestamp_ms': 1762654206594, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'WVIMTAFGVPTPLMHC3CQBITNI', 'name': 'projects/ext-datasets/operations/WVIMTAFGVPTPLMHC3CQBITNI'}\n","Grid 60\n","curr_year 2017\n","Saving data for Murshidabad 2017\n","Task Started {'state': 'READY', 'description': 'Murshidabad_60_2017', 'priority': 100, 'creation_timestamp_ms': 1762654213512, 'update_timestamp_ms': 1762654213512, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'YUVX5V4UVUIULAQRELZGWCCZ', 'name': 'projects/ext-datasets/operations/YUVX5V4UVUIULAQRELZGWCCZ'}\n","Grid 61\n","curr_year 2017\n","Saving data for Murshidabad 2017\n","Task Started {'state': 'READY', 'description': 'Murshidabad_61_2017', 'priority': 100, 'creation_timestamp_ms': 1762654221501, 'update_timestamp_ms': 1762654221501, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'ININU7K5RYMHTU45UCPHJZIE', 'name': 'projects/ext-datasets/operations/ININU7K5RYMHTU45UCPHJZIE'}\n","Grid 62\n","curr_year 2017\n","Saving data for Murshidabad 2017\n","Task Started {'state': 'READY', 'description': 'Murshidabad_62_2017', 'priority': 100, 'creation_timestamp_ms': 1762654229227, 'update_timestamp_ms': 1762654229227, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '4XB3RDH2RLZT4O3N6PFVNF6I', 'name': 'projects/ext-datasets/operations/4XB3RDH2RLZT4O3N6PFVNF6I'}\n","Grid 63\n","curr_year 2017\n","Saving data for Murshidabad 2017\n","Task Started {'state': 'READY', 'description': 'Murshidabad_63_2017', 'priority': 100, 'creation_timestamp_ms': 1762654234955, 'update_timestamp_ms': 1762654234955, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'VXQYA4V7ULZKEIKMHPMXRGVE', 'name': 'projects/ext-datasets/operations/VXQYA4V7ULZKEIKMHPMXRGVE'}\n","Grid 64\n","curr_year 2017\n","Saving data for Murshidabad 2017\n","Task Started {'state': 'READY', 'description': 'Murshidabad_64_2017', 'priority': 100, 'creation_timestamp_ms': 1762654242660, 'update_timestamp_ms': 1762654242660, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'TS33FE4ICXVIA25FBCHQ2QEB', 'name': 'projects/ext-datasets/operations/TS33FE4ICXVIA25FBCHQ2QEB'}\n","Grid 65\n","curr_year 2017\n","Saving data for Murshidabad 2017\n","Task Started {'state': 'READY', 'description': 'Murshidabad_65_2017', 'priority': 100, 'creation_timestamp_ms': 1762654251350, 'update_timestamp_ms': 1762654251350, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'QIESXKNY7CRZCN5QX34XK6N7', 'name': 'projects/ext-datasets/operations/QIESXKNY7CRZCN5QX34XK6N7'}\n","Grid 66\n","curr_year 2017\n","Saving data for Murshidabad 2017\n","Task Started {'state': 'READY', 'description': 'Murshidabad_66_2017', 'priority': 100, 'creation_timestamp_ms': 1762654259090, 'update_timestamp_ms': 1762654259090, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'G2ZNNHIMPD2ZOL6UWBB2QSE4', 'name': 'projects/ext-datasets/operations/G2ZNNHIMPD2ZOL6UWBB2QSE4'}\n","Grid 67\n","curr_year 2017\n","Saving data for Murshidabad 2017\n","Task Started {'state': 'READY', 'description': 'Murshidabad_67_2017', 'priority': 100, 'creation_timestamp_ms': 1762654266099, 'update_timestamp_ms': 1762654266099, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'ZHT2OMTNF43VE7DM5WYCLB6K', 'name': 'projects/ext-datasets/operations/ZHT2OMTNF43VE7DM5WYCLB6K'}\n","Grid 68\n","curr_year 2017\n","Saving data for Murshidabad 2017\n","Task Started {'state': 'READY', 'description': 'Murshidabad_68_2017', 'priority': 100, 'creation_timestamp_ms': 1762654274490, 'update_timestamp_ms': 1762654274490, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'LXH2NTPT4WO4334Q5K3G6REW', 'name': 'projects/ext-datasets/operations/LXH2NTPT4WO4334Q5K3G6REW'}\n","Grid 69\n","curr_year 2017\n","Saving data for Murshidabad 2017\n","Task Started {'state': 'READY', 'description': 'Murshidabad_69_2017', 'priority': 100, 'creation_timestamp_ms': 1762654281865, 'update_timestamp_ms': 1762654281865, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'IHJG6HO5JXBURXH6QIY52ZXB', 'name': 'projects/ext-datasets/operations/IHJG6HO5JXBURXH6QIY52ZXB'}\n","Grid 70\n","curr_year 2017\n","Saving data for Murshidabad 2017\n","Task Started {'state': 'READY', 'description': 'Murshidabad_70_2017', 'priority': 100, 'creation_timestamp_ms': 1762654288001, 'update_timestamp_ms': 1762654288001, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'Z64IXTFYHMT4YP2FTWTAF4BQ', 'name': 'projects/ext-datasets/operations/Z64IXTFYHMT4YP2FTWTAF4BQ'}\n","Grid 71\n","curr_year 2017\n","Saving data for Murshidabad 2017\n","Task Started {'state': 'READY', 'description': 'Murshidabad_71_2017', 'priority': 100, 'creation_timestamp_ms': 1762654295009, 'update_timestamp_ms': 1762654295009, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '6U4HLKEPGFFPGTUECDNENYSI', 'name': 'projects/ext-datasets/operations/6U4HLKEPGFFPGTUECDNENYSI'}\n","Grid 72\n","curr_year 2017\n","Saving data for Murshidabad 2017\n","Task Started {'state': 'READY', 'description': 'Murshidabad_72_2017', 'priority': 100, 'creation_timestamp_ms': 1762654302138, 'update_timestamp_ms': 1762654302138, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'IQDVX5WSXQ7E5WU6TWYRDJRI', 'name': 'projects/ext-datasets/operations/IQDVX5WSXQ7E5WU6TWYRDJRI'}\n","Grid 73\n","curr_year 2017\n","Saving data for Murshidabad 2017\n","Task Started {'state': 'READY', 'description': 'Murshidabad_73_2017', 'priority': 100, 'creation_timestamp_ms': 1762654309416, 'update_timestamp_ms': 1762654309416, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'VLQQ6MSCOAGR7XOF7PJILBJY', 'name': 'projects/ext-datasets/operations/VLQQ6MSCOAGR7XOF7PJILBJY'}\n","Grid 74\n","curr_year 2017\n","Saving data for Murshidabad 2017\n","Task Started {'state': 'READY', 'description': 'Murshidabad_74_2017', 'priority': 100, 'creation_timestamp_ms': 1762654316819, 'update_timestamp_ms': 1762654316819, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'ZYHSYAVWZCJPNH47ZHL4YJLJ', 'name': 'projects/ext-datasets/operations/ZYHSYAVWZCJPNH47ZHL4YJLJ'}\n","Grid 75\n","curr_year 2017\n","Saving data for Murshidabad 2017\n","Task Started {'state': 'READY', 'description': 'Murshidabad_75_2017', 'priority': 100, 'creation_timestamp_ms': 1762654324497, 'update_timestamp_ms': 1762654324497, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'HHCUJR4DOHQTWZYVJC3VPQHB', 'name': 'projects/ext-datasets/operations/HHCUJR4DOHQTWZYVJC3VPQHB'}\n","Grid 76\n","curr_year 2017\n","Saving data for Murshidabad 2017\n","Task Started {'state': 'READY', 'description': 'Murshidabad_76_2017', 'priority': 100, 'creation_timestamp_ms': 1762654333435, 'update_timestamp_ms': 1762654333435, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '2NXHHIZTAWMUIP2ONH2WKRNK', 'name': 'projects/ext-datasets/operations/2NXHHIZTAWMUIP2ONH2WKRNK'}\n","Grid 77\n","curr_year 2017\n","Saving data for Murshidabad 2017\n","Task Started {'state': 'READY', 'description': 'Murshidabad_77_2017', 'priority': 100, 'creation_timestamp_ms': 1762654340665, 'update_timestamp_ms': 1762654340665, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'PA7N3M6BT5NFEOYVZTT2RF3E', 'name': 'projects/ext-datasets/operations/PA7N3M6BT5NFEOYVZTT2RF3E'}\n","Grid 78\n","curr_year 2017\n","Saving data for Murshidabad 2017\n","Task Started {'state': 'READY', 'description': 'Murshidabad_78_2017', 'priority': 100, 'creation_timestamp_ms': 1762654355061, 'update_timestamp_ms': 1762654355061, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '4LZYVKDAFXDBAZAZCMAD5HJS', 'name': 'projects/ext-datasets/operations/4LZYVKDAFXDBAZAZCMAD5HJS'}\n","Grid 79\n","curr_year 2017\n","Saving data for Murshidabad 2017\n","Task Started {'state': 'READY', 'description': 'Murshidabad_79_2017', 'priority': 100, 'creation_timestamp_ms': 1762654363095, 'update_timestamp_ms': 1762654363095, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'BZ5UIKJ4MH62FMCDX4RDPB6N', 'name': 'projects/ext-datasets/operations/BZ5UIKJ4MH62FMCDX4RDPB6N'}\n","Grid 80\n","curr_year 2017\n","Saving data for Murshidabad 2017\n","Task Started {'state': 'READY', 'description': 'Murshidabad_80_2017', 'priority': 100, 'creation_timestamp_ms': 1762654367787, 'update_timestamp_ms': 1762654367787, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'IZSLOUU4TXEVO65BL6F6A6TI', 'name': 'projects/ext-datasets/operations/IZSLOUU4TXEVO65BL6F6A6TI'}\n","Grid 81\n","curr_year 2017\n","Saving data for Murshidabad 2017\n","Task Started {'state': 'READY', 'description': 'Murshidabad_81_2017', 'priority': 100, 'creation_timestamp_ms': 1762654373562, 'update_timestamp_ms': 1762654373562, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '7CLOGSI2U3AVSKGXPAAOUUYO', 'name': 'projects/ext-datasets/operations/7CLOGSI2U3AVSKGXPAAOUUYO'}\n","Grid 82\n","curr_year 2017\n","Saving data for Murshidabad 2017\n","Task Started {'state': 'READY', 'description': 'Murshidabad_82_2017', 'priority': 100, 'creation_timestamp_ms': 1762654380912, 'update_timestamp_ms': 1762654380912, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'LAIUHPT4O2BI27Z6XUYATFCI', 'name': 'projects/ext-datasets/operations/LAIUHPT4O2BI27Z6XUYATFCI'}\n","Grid 83\n","curr_year 2017\n","Saving data for Murshidabad 2017\n","Task Started {'state': 'READY', 'description': 'Murshidabad_83_2017', 'priority': 100, 'creation_timestamp_ms': 1762654386601, 'update_timestamp_ms': 1762654386601, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '4A4QTYVS3JCDIL7WNKOCQOJC', 'name': 'projects/ext-datasets/operations/4A4QTYVS3JCDIL7WNKOCQOJC'}\n","Grid 84\n","curr_year 2017\n","Saving data for Murshidabad 2017\n","Task Started {'state': 'READY', 'description': 'Murshidabad_84_2017', 'priority': 100, 'creation_timestamp_ms': 1762654393177, 'update_timestamp_ms': 1762654393177, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'AWH4WDKEPAPR2NUVUO55REA2', 'name': 'projects/ext-datasets/operations/AWH4WDKEPAPR2NUVUO55REA2'}\n","Grid 85\n","curr_year 2017\n","Saving data for Murshidabad 2017\n","Task Started {'state': 'READY', 'description': 'Murshidabad_85_2017', 'priority': 100, 'creation_timestamp_ms': 1762654399017, 'update_timestamp_ms': 1762654399017, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'PTZJ333PZBVDKFMJ2CA2B27P', 'name': 'projects/ext-datasets/operations/PTZJ333PZBVDKFMJ2CA2B27P'}\n","Grid 86\n","curr_year 2017\n","Saving data for Murshidabad 2017\n","Task Started {'state': 'READY', 'description': 'Murshidabad_86_2017', 'priority': 100, 'creation_timestamp_ms': 1762654405087, 'update_timestamp_ms': 1762654405087, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'NHJPNXCKQABNHNA7FTKI47K2', 'name': 'projects/ext-datasets/operations/NHJPNXCKQABNHNA7FTKI47K2'}\n","4858.730777263641\n","Year 2017, District 21: Nadia, grids: 72\n","Grid 0\n","curr_year 2017\n","Saving data for Nadia 2017\n","Task Started {'state': 'READY', 'description': 'Nadia_0_2017', 'priority': 100, 'creation_timestamp_ms': 1762654419792, 'update_timestamp_ms': 1762654419792, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'P5PGIJ6O6ZOCYR5GPDTFUSGM', 'name': 'projects/ext-datasets/operations/P5PGIJ6O6ZOCYR5GPDTFUSGM'}\n","Grid 1\n","curr_year 2017\n","Saving data for Nadia 2017\n","Task Started {'state': 'READY', 'description': 'Nadia_1_2017', 'priority': 100, 'creation_timestamp_ms': 1762654428290, 'update_timestamp_ms': 1762654428290, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'EV7E5A62P67TQE7K7H72GMHI', 'name': 'projects/ext-datasets/operations/EV7E5A62P67TQE7K7H72GMHI'}\n","Grid 2\n","curr_year 2017\n","Saving data for Nadia 2017\n","Task Started {'state': 'READY', 'description': 'Nadia_2_2017', 'priority': 100, 'creation_timestamp_ms': 1762654436050, 'update_timestamp_ms': 1762654436050, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '4AVZ7E3WSF7MZQ5SRC22BDN5', 'name': 'projects/ext-datasets/operations/4AVZ7E3WSF7MZQ5SRC22BDN5'}\n","Grid 3\n","curr_year 2017\n","Saving data for Nadia 2017\n","Task Started {'state': 'READY', 'description': 'Nadia_3_2017', 'priority': 100, 'creation_timestamp_ms': 1762654442743, 'update_timestamp_ms': 1762654442743, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'ZQODKJ3S5ANYKJQYAXBL447K', 'name': 'projects/ext-datasets/operations/ZQODKJ3S5ANYKJQYAXBL447K'}\n","Grid 4\n","curr_year 2017\n","Saving data for Nadia 2017\n","Task Started {'state': 'READY', 'description': 'Nadia_4_2017', 'priority': 100, 'creation_timestamp_ms': 1762654448632, 'update_timestamp_ms': 1762654448632, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'XNYFGFYQFEOSIAKVFEE7W6XU', 'name': 'projects/ext-datasets/operations/XNYFGFYQFEOSIAKVFEE7W6XU'}\n","Grid 5\n","curr_year 2017\n","Saving data for Nadia 2017\n","Task Started {'state': 'READY', 'description': 'Nadia_5_2017', 'priority': 100, 'creation_timestamp_ms': 1762654455185, 'update_timestamp_ms': 1762654455185, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '3BXQRHRNSWRULT4HJMA3CN6R', 'name': 'projects/ext-datasets/operations/3BXQRHRNSWRULT4HJMA3CN6R'}\n","Grid 6\n","curr_year 2017\n","Saving data for Nadia 2017\n","Task Started {'state': 'READY', 'description': 'Nadia_6_2017', 'priority': 100, 'creation_timestamp_ms': 1762654462176, 'update_timestamp_ms': 1762654462176, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'Q77RUTGWKSFVUYJCEDQXB6XR', 'name': 'projects/ext-datasets/operations/Q77RUTGWKSFVUYJCEDQXB6XR'}\n","Grid 7\n","curr_year 2017\n","Saving data for Nadia 2017\n","Task Started {'state': 'READY', 'description': 'Nadia_7_2017', 'priority': 100, 'creation_timestamp_ms': 1762654468567, 'update_timestamp_ms': 1762654468567, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'SOHWXOHUHGMGRQVYC3LRXY7I', 'name': 'projects/ext-datasets/operations/SOHWXOHUHGMGRQVYC3LRXY7I'}\n","Grid 8\n","curr_year 2017\n","Saving data for Nadia 2017\n","Task Started {'state': 'READY', 'description': 'Nadia_8_2017', 'priority': 100, 'creation_timestamp_ms': 1762654476363, 'update_timestamp_ms': 1762654476363, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '2N3M5A4DU65YQXAZEEAGWDBO', 'name': 'projects/ext-datasets/operations/2N3M5A4DU65YQXAZEEAGWDBO'}\n","Grid 9\n","curr_year 2017\n","Saving data for Nadia 2017\n","Task Started {'state': 'READY', 'description': 'Nadia_9_2017', 'priority': 100, 'creation_timestamp_ms': 1762654482096, 'update_timestamp_ms': 1762654482096, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'W7DJDN6AT7R73UQCRTXHUPLX', 'name': 'projects/ext-datasets/operations/W7DJDN6AT7R73UQCRTXHUPLX'}\n","Grid 10\n","curr_year 2017\n","Saving data for Nadia 2017\n","Task Started {'state': 'READY', 'description': 'Nadia_10_2017', 'priority': 100, 'creation_timestamp_ms': 1762654489169, 'update_timestamp_ms': 1762654489169, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'JAZE4BTT7ULXR4AW22IC4IA5', 'name': 'projects/ext-datasets/operations/JAZE4BTT7ULXR4AW22IC4IA5'}\n","Grid 11\n","curr_year 2017\n","Saving data for Nadia 2017\n","Task Started {'state': 'READY', 'description': 'Nadia_11_2017', 'priority': 100, 'creation_timestamp_ms': 1762654494392, 'update_timestamp_ms': 1762654494392, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'P5CR4MTND7KHWX6MBWDVSDX6', 'name': 'projects/ext-datasets/operations/P5CR4MTND7KHWX6MBWDVSDX6'}\n","Grid 12\n","curr_year 2017\n","Saving data for Nadia 2017\n","Task Started {'state': 'READY', 'description': 'Nadia_12_2017', 'priority': 100, 'creation_timestamp_ms': 1762654507057, 'update_timestamp_ms': 1762654507057, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'LG6MCIURPMHJE72WS37BF67W', 'name': 'projects/ext-datasets/operations/LG6MCIURPMHJE72WS37BF67W'}\n","Grid 13\n","curr_year 2017\n","Saving data for Nadia 2017\n","Task Started {'state': 'READY', 'description': 'Nadia_13_2017', 'priority': 100, 'creation_timestamp_ms': 1762654510762, 'update_timestamp_ms': 1762654510762, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'Q62SVQJXSOLW33DSXWEA54WH', 'name': 'projects/ext-datasets/operations/Q62SVQJXSOLW33DSXWEA54WH'}\n","Grid 14\n","curr_year 2017\n","Saving data for Nadia 2017\n","Task Started {'state': 'READY', 'description': 'Nadia_14_2017', 'priority': 100, 'creation_timestamp_ms': 1762654518502, 'update_timestamp_ms': 1762654518502, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'NOV7L45SQCUNMKRPUY6WRTG2', 'name': 'projects/ext-datasets/operations/NOV7L45SQCUNMKRPUY6WRTG2'}\n","Grid 15\n","curr_year 2017\n","Saving data for Nadia 2017\n","Task Started {'state': 'READY', 'description': 'Nadia_15_2017', 'priority': 100, 'creation_timestamp_ms': 1762654523429, 'update_timestamp_ms': 1762654523429, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'ZBKSXJEJEZ3CMYOJSNRBYOPN', 'name': 'projects/ext-datasets/operations/ZBKSXJEJEZ3CMYOJSNRBYOPN'}\n","Grid 16\n","curr_year 2017\n","Saving data for Nadia 2017\n","Task Started {'state': 'READY', 'description': 'Nadia_16_2017', 'priority': 100, 'creation_timestamp_ms': 1762654532988, 'update_timestamp_ms': 1762654532988, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'N7IIJR5LMYZRNWOZ7IHY34XE', 'name': 'projects/ext-datasets/operations/N7IIJR5LMYZRNWOZ7IHY34XE'}\n","Grid 17\n","curr_year 2017\n","Saving data for Nadia 2017\n","Task Started {'state': 'READY', 'description': 'Nadia_17_2017', 'priority': 100, 'creation_timestamp_ms': 1762654545412, 'update_timestamp_ms': 1762654545412, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'IFWAH4H5QTNH6PORAP5DRIKG', 'name': 'projects/ext-datasets/operations/IFWAH4H5QTNH6PORAP5DRIKG'}\n","Grid 18\n","curr_year 2017\n","Saving data for Nadia 2017\n","Task Started {'state': 'READY', 'description': 'Nadia_18_2017', 'priority': 100, 'creation_timestamp_ms': 1762654552007, 'update_timestamp_ms': 1762654552007, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'UQOIY7PFUKS5XT7DSBV2RCNO', 'name': 'projects/ext-datasets/operations/UQOIY7PFUKS5XT7DSBV2RCNO'}\n","Grid 19\n","curr_year 2017\n","Saving data for Nadia 2017\n","Task Started {'state': 'READY', 'description': 'Nadia_19_2017', 'priority': 100, 'creation_timestamp_ms': 1762654560385, 'update_timestamp_ms': 1762654560385, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '5ODI7Q3VCCSI2CDX6VSTEJSX', 'name': 'projects/ext-datasets/operations/5ODI7Q3VCCSI2CDX6VSTEJSX'}\n","Grid 20\n","curr_year 2017\n","Saving data for Nadia 2017\n","Task Started {'state': 'READY', 'description': 'Nadia_20_2017', 'priority': 100, 'creation_timestamp_ms': 1762654569196, 'update_timestamp_ms': 1762654569196, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'K3HI2V3ZNVQVHDBITC7KIAH3', 'name': 'projects/ext-datasets/operations/K3HI2V3ZNVQVHDBITC7KIAH3'}\n","Grid 21\n","curr_year 2017\n","Saving data for Nadia 2017\n","Task Started {'state': 'READY', 'description': 'Nadia_21_2017', 'priority': 100, 'creation_timestamp_ms': 1762654578627, 'update_timestamp_ms': 1762654578627, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'BA67H25NCB5XSZWY4VYBJRFR', 'name': 'projects/ext-datasets/operations/BA67H25NCB5XSZWY4VYBJRFR'}\n","Grid 22\n","curr_year 2017\n","Saving data for Nadia 2017\n","Task Started {'state': 'READY', 'description': 'Nadia_22_2017', 'priority': 100, 'creation_timestamp_ms': 1762654585544, 'update_timestamp_ms': 1762654585544, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'YHSQSRDRJV65EYXLCUXD5G2K', 'name': 'projects/ext-datasets/operations/YHSQSRDRJV65EYXLCUXD5G2K'}\n","Grid 23\n","curr_year 2017\n","Saving data for Nadia 2017\n","Task Started {'state': 'READY', 'description': 'Nadia_23_2017', 'priority': 100, 'creation_timestamp_ms': 1762654592409, 'update_timestamp_ms': 1762654592409, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'IURVCPQ472HQXBMIR2WKMMPW', 'name': 'projects/ext-datasets/operations/IURVCPQ472HQXBMIR2WKMMPW'}\n","Grid 24\n","curr_year 2017\n","Saving data for Nadia 2017\n","Task Started {'state': 'READY', 'description': 'Nadia_24_2017', 'priority': 100, 'creation_timestamp_ms': 1762654598493, 'update_timestamp_ms': 1762654598493, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'LMKFYNE6L3XZKRID6G4MI26H', 'name': 'projects/ext-datasets/operations/LMKFYNE6L3XZKRID6G4MI26H'}\n","Grid 25\n","curr_year 2017\n","Saving data for Nadia 2017\n","Task Started {'state': 'READY', 'description': 'Nadia_25_2017', 'priority': 100, 'creation_timestamp_ms': 1762654608846, 'update_timestamp_ms': 1762654608846, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'ESSMALVXLSZGFXYDHOG572TM', 'name': 'projects/ext-datasets/operations/ESSMALVXLSZGFXYDHOG572TM'}\n","Grid 26\n","curr_year 2017\n","Saving data for Nadia 2017\n","Task Started {'state': 'READY', 'description': 'Nadia_26_2017', 'priority': 100, 'creation_timestamp_ms': 1762654614074, 'update_timestamp_ms': 1762654614074, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'R4B5FL2FNRE33XDQ5WTJD6PS', 'name': 'projects/ext-datasets/operations/R4B5FL2FNRE33XDQ5WTJD6PS'}\n","Grid 27\n","curr_year 2017\n","Saving data for Nadia 2017\n","Task Started {'state': 'READY', 'description': 'Nadia_27_2017', 'priority': 100, 'creation_timestamp_ms': 1762654620875, 'update_timestamp_ms': 1762654620875, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'CJ64I3PTFXQXK2AH5PNH26NK', 'name': 'projects/ext-datasets/operations/CJ64I3PTFXQXK2AH5PNH26NK'}\n","Grid 28\n","curr_year 2017\n","Saving data for Nadia 2017\n","Task Started {'state': 'READY', 'description': 'Nadia_28_2017', 'priority': 100, 'creation_timestamp_ms': 1762654631431, 'update_timestamp_ms': 1762654631431, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '3VBOFEDHHXMHQSTTTJWI3P2S', 'name': 'projects/ext-datasets/operations/3VBOFEDHHXMHQSTTTJWI3P2S'}\n","Grid 29\n","curr_year 2017\n","Saving data for Nadia 2017\n","Task Started {'state': 'READY', 'description': 'Nadia_29_2017', 'priority': 100, 'creation_timestamp_ms': 1762654640823, 'update_timestamp_ms': 1762654640823, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '2TAVARUICMYDVATVCAYUY3OD', 'name': 'projects/ext-datasets/operations/2TAVARUICMYDVATVCAYUY3OD'}\n","Grid 30\n","curr_year 2017\n","Saving data for Nadia 2017\n","Task Started {'state': 'READY', 'description': 'Nadia_30_2017', 'priority': 100, 'creation_timestamp_ms': 1762654648097, 'update_timestamp_ms': 1762654648097, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'BT2DVMSYFWV7HFUAFGMFUE3O', 'name': 'projects/ext-datasets/operations/BT2DVMSYFWV7HFUAFGMFUE3O'}\n","Grid 31\n","curr_year 2017\n","Saving data for Nadia 2017\n","Task Started {'state': 'READY', 'description': 'Nadia_31_2017', 'priority': 100, 'creation_timestamp_ms': 1762654656171, 'update_timestamp_ms': 1762654656171, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'BR7YIKUSAEU46WPPX4MSJFFQ', 'name': 'projects/ext-datasets/operations/BR7YIKUSAEU46WPPX4MSJFFQ'}\n","Grid 32\n","curr_year 2017\n","Saving data for Nadia 2017\n","Task Started {'state': 'READY', 'description': 'Nadia_32_2017', 'priority': 100, 'creation_timestamp_ms': 1762654663746, 'update_timestamp_ms': 1762654663746, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'AG5VIAECDXES6USZKPVVJ5BO', 'name': 'projects/ext-datasets/operations/AG5VIAECDXES6USZKPVVJ5BO'}\n","Grid 33\n","curr_year 2017\n","Saving data for Nadia 2017\n","Task Started {'state': 'READY', 'description': 'Nadia_33_2017', 'priority': 100, 'creation_timestamp_ms': 1762654673967, 'update_timestamp_ms': 1762654673967, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '6NZMVZGPB5T5R4Y6Y2OVDK43', 'name': 'projects/ext-datasets/operations/6NZMVZGPB5T5R4Y6Y2OVDK43'}\n","Grid 34\n","curr_year 2017\n","Saving data for Nadia 2017\n","Task Started {'state': 'READY', 'description': 'Nadia_34_2017', 'priority': 100, 'creation_timestamp_ms': 1762654682863, 'update_timestamp_ms': 1762654682863, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'J2F2C4UPFWOTRU4URM36FEUH', 'name': 'projects/ext-datasets/operations/J2F2C4UPFWOTRU4URM36FEUH'}\n","Grid 35\n","curr_year 2017\n","Saving data for Nadia 2017\n","Task Started {'state': 'READY', 'description': 'Nadia_35_2017', 'priority': 100, 'creation_timestamp_ms': 1762654688596, 'update_timestamp_ms': 1762654688596, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'EUJEKUNXV2E6DCPHQDF5WYXR', 'name': 'projects/ext-datasets/operations/EUJEKUNXV2E6DCPHQDF5WYXR'}\n","Grid 36\n","curr_year 2017\n","Saving data for Nadia 2017\n","Task Started {'state': 'READY', 'description': 'Nadia_36_2017', 'priority': 100, 'creation_timestamp_ms': 1762654697025, 'update_timestamp_ms': 1762654697025, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'RSLDAJITOU6XBMMLFH4WIREI', 'name': 'projects/ext-datasets/operations/RSLDAJITOU6XBMMLFH4WIREI'}\n","Grid 37\n","curr_year 2017\n","Saving data for Nadia 2017\n","Task Started {'state': 'READY', 'description': 'Nadia_37_2017', 'priority': 100, 'creation_timestamp_ms': 1762654703898, 'update_timestamp_ms': 1762654703898, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '3MN5ODMGPRQQP3D5TRYPT23E', 'name': 'projects/ext-datasets/operations/3MN5ODMGPRQQP3D5TRYPT23E'}\n","Grid 38\n","curr_year 2017\n","Saving data for Nadia 2017\n","Task Started {'state': 'READY', 'description': 'Nadia_38_2017', 'priority': 100, 'creation_timestamp_ms': 1762654708866, 'update_timestamp_ms': 1762654708866, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'CR45V4BGQRBTEHIX7ZCRNQRU', 'name': 'projects/ext-datasets/operations/CR45V4BGQRBTEHIX7ZCRNQRU'}\n","Grid 39\n","curr_year 2017\n","Saving data for Nadia 2017\n","Task Started {'state': 'READY', 'description': 'Nadia_39_2017', 'priority': 100, 'creation_timestamp_ms': 1762654716825, 'update_timestamp_ms': 1762654716825, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'LTNFHQHZQHYMRFNFV5JO5K3J', 'name': 'projects/ext-datasets/operations/LTNFHQHZQHYMRFNFV5JO5K3J'}\n","Grid 40\n","curr_year 2017\n","Saving data for Nadia 2017\n","Task Started {'state': 'READY', 'description': 'Nadia_40_2017', 'priority': 100, 'creation_timestamp_ms': 1762654725729, 'update_timestamp_ms': 1762654725729, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '4TODRBKH5ZRCB5EPJCEJUYMH', 'name': 'projects/ext-datasets/operations/4TODRBKH5ZRCB5EPJCEJUYMH'}\n","Grid 41\n","curr_year 2017\n","Saving data for Nadia 2017\n","Task Started {'state': 'READY', 'description': 'Nadia_41_2017', 'priority': 100, 'creation_timestamp_ms': 1762654736107, 'update_timestamp_ms': 1762654736107, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '4AOARY2SLRNTYXLJORE3NG4J', 'name': 'projects/ext-datasets/operations/4AOARY2SLRNTYXLJORE3NG4J'}\n","Grid 42\n","curr_year 2017\n","Saving data for Nadia 2017\n","Task Started {'state': 'READY', 'description': 'Nadia_42_2017', 'priority': 100, 'creation_timestamp_ms': 1762654744870, 'update_timestamp_ms': 1762654744870, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'VZFPYELGR7JPXWZIX7ZSRPBT', 'name': 'projects/ext-datasets/operations/VZFPYELGR7JPXWZIX7ZSRPBT'}\n","Grid 43\n","curr_year 2017\n","Saving data for Nadia 2017\n","Task Started {'state': 'READY', 'description': 'Nadia_43_2017', 'priority': 100, 'creation_timestamp_ms': 1762654751181, 'update_timestamp_ms': 1762654751181, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'S2HCJSHR54YV77OBCXQI3DOJ', 'name': 'projects/ext-datasets/operations/S2HCJSHR54YV77OBCXQI3DOJ'}\n","Grid 44\n","curr_year 2017\n","Saving data for Nadia 2017\n","Task Started {'state': 'READY', 'description': 'Nadia_44_2017', 'priority': 100, 'creation_timestamp_ms': 1762654762793, 'update_timestamp_ms': 1762654762793, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '3XVLYS5J2BHIUDHLKQPBX3SU', 'name': 'projects/ext-datasets/operations/3XVLYS5J2BHIUDHLKQPBX3SU'}\n","Grid 45\n","curr_year 2017\n","Saving data for Nadia 2017\n","Task Started {'state': 'READY', 'description': 'Nadia_45_2017', 'priority': 100, 'creation_timestamp_ms': 1762654769048, 'update_timestamp_ms': 1762654769048, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'BGT53AHBJXRE6EO47BFSMU7Q', 'name': 'projects/ext-datasets/operations/BGT53AHBJXRE6EO47BFSMU7Q'}\n","Grid 46\n","curr_year 2017\n","Saving data for Nadia 2017\n","Task Started {'state': 'READY', 'description': 'Nadia_46_2017', 'priority': 100, 'creation_timestamp_ms': 1762654775783, 'update_timestamp_ms': 1762654775783, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'L2T64P3RVYPMN2R4F7QPLPAS', 'name': 'projects/ext-datasets/operations/L2T64P3RVYPMN2R4F7QPLPAS'}\n","Grid 47\n","curr_year 2017\n","Saving data for Nadia 2017\n","Task Started {'state': 'READY', 'description': 'Nadia_47_2017', 'priority': 100, 'creation_timestamp_ms': 1762654784493, 'update_timestamp_ms': 1762654784493, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'KQR6772HX4KTWYGY64G2PTMS', 'name': 'projects/ext-datasets/operations/KQR6772HX4KTWYGY64G2PTMS'}\n","Grid 48\n","curr_year 2017\n","Saving data for Nadia 2017\n","Task Started {'state': 'READY', 'description': 'Nadia_48_2017', 'priority': 100, 'creation_timestamp_ms': 1762654793860, 'update_timestamp_ms': 1762654793860, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'ZYRQMDUCXAPDCNOMN44ICPNZ', 'name': 'projects/ext-datasets/operations/ZYRQMDUCXAPDCNOMN44ICPNZ'}\n","Grid 49\n","curr_year 2017\n","Saving data for Nadia 2017\n","Task Started {'state': 'READY', 'description': 'Nadia_49_2017', 'priority': 100, 'creation_timestamp_ms': 1762654801999, 'update_timestamp_ms': 1762654801999, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'UGG75TWLKBDKU7BBKFYT2K3L', 'name': 'projects/ext-datasets/operations/UGG75TWLKBDKU7BBKFYT2K3L'}\n","Grid 50\n","curr_year 2017\n","Saving data for Nadia 2017\n","Task Started {'state': 'READY', 'description': 'Nadia_50_2017', 'priority': 100, 'creation_timestamp_ms': 1762654809058, 'update_timestamp_ms': 1762654809058, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'DBI3JVZTUKCZ6WFTHDYMYE72', 'name': 'projects/ext-datasets/operations/DBI3JVZTUKCZ6WFTHDYMYE72'}\n","Grid 51\n","curr_year 2017\n","Saving data for Nadia 2017\n","Task Started {'state': 'READY', 'description': 'Nadia_51_2017', 'priority': 100, 'creation_timestamp_ms': 1762654815411, 'update_timestamp_ms': 1762654815411, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'YYQUFHIO7ZCVXTWCRCBV4RKY', 'name': 'projects/ext-datasets/operations/YYQUFHIO7ZCVXTWCRCBV4RKY'}\n","Grid 52\n","curr_year 2017\n","Saving data for Nadia 2017\n","Task Started {'state': 'READY', 'description': 'Nadia_52_2017', 'priority': 100, 'creation_timestamp_ms': 1762654820819, 'update_timestamp_ms': 1762654820819, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'WXHMRD53TXSEE2T4DNQCZLKL', 'name': 'projects/ext-datasets/operations/WXHMRD53TXSEE2T4DNQCZLKL'}\n","Grid 53\n","curr_year 2017\n","Saving data for Nadia 2017\n","Task Started {'state': 'READY', 'description': 'Nadia_53_2017', 'priority': 100, 'creation_timestamp_ms': 1762654826365, 'update_timestamp_ms': 1762654826365, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'BMZSET7SI2P6SZT3O2GTTC6Q', 'name': 'projects/ext-datasets/operations/BMZSET7SI2P6SZT3O2GTTC6Q'}\n","Grid 54\n","curr_year 2017\n","Saving data for Nadia 2017\n","Task Started {'state': 'READY', 'description': 'Nadia_54_2017', 'priority': 100, 'creation_timestamp_ms': 1762654835460, 'update_timestamp_ms': 1762654835460, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'DJ7QBEERICXDVW7S3EFUSEGQ', 'name': 'projects/ext-datasets/operations/DJ7QBEERICXDVW7S3EFUSEGQ'}\n","Grid 55\n","curr_year 2017\n","Saving data for Nadia 2017\n","Task Started {'state': 'READY', 'description': 'Nadia_55_2017', 'priority': 100, 'creation_timestamp_ms': 1762654841227, 'update_timestamp_ms': 1762654841227, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'DZY3TALCKANBXFPZ3YKQ5GZP', 'name': 'projects/ext-datasets/operations/DZY3TALCKANBXFPZ3YKQ5GZP'}\n","Grid 56\n","curr_year 2017\n","Saving data for Nadia 2017\n","Task Started {'state': 'READY', 'description': 'Nadia_56_2017', 'priority': 100, 'creation_timestamp_ms': 1762654847526, 'update_timestamp_ms': 1762654847526, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'Y4ZBUKN5QJGU5BBU3XJPS3VZ', 'name': 'projects/ext-datasets/operations/Y4ZBUKN5QJGU5BBU3XJPS3VZ'}\n","Grid 57\n","curr_year 2017\n","Saving data for Nadia 2017\n","Task Started {'state': 'READY', 'description': 'Nadia_57_2017', 'priority': 100, 'creation_timestamp_ms': 1762654853996, 'update_timestamp_ms': 1762654853996, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'LZZI4UADWIEV6DEFWGJUVMET', 'name': 'projects/ext-datasets/operations/LZZI4UADWIEV6DEFWGJUVMET'}\n","Grid 58\n","curr_year 2017\n","Saving data for Nadia 2017\n","Task Started {'state': 'READY', 'description': 'Nadia_58_2017', 'priority': 100, 'creation_timestamp_ms': 1762654861628, 'update_timestamp_ms': 1762654861628, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'TNCY45SMZQLYF37UOTZC6TLT', 'name': 'projects/ext-datasets/operations/TNCY45SMZQLYF37UOTZC6TLT'}\n","Grid 59\n","curr_year 2017\n","Saving data for Nadia 2017\n","Task Started {'state': 'READY', 'description': 'Nadia_59_2017', 'priority': 100, 'creation_timestamp_ms': 1762654867107, 'update_timestamp_ms': 1762654867107, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'TG2IIJXIUJBET3N5PBXDZ6L4', 'name': 'projects/ext-datasets/operations/TG2IIJXIUJBET3N5PBXDZ6L4'}\n","Grid 60\n","curr_year 2017\n","Saving data for Nadia 2017\n","Task Started {'state': 'READY', 'description': 'Nadia_60_2017', 'priority': 100, 'creation_timestamp_ms': 1762654877080, 'update_timestamp_ms': 1762654877080, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '62FQR26OJQKQRQZRQ3C2LQOW', 'name': 'projects/ext-datasets/operations/62FQR26OJQKQRQZRQ3C2LQOW'}\n","Grid 61\n","curr_year 2017\n","Saving data for Nadia 2017\n","Task Started {'state': 'READY', 'description': 'Nadia_61_2017', 'priority': 100, 'creation_timestamp_ms': 1762654883093, 'update_timestamp_ms': 1762654883093, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '6HX4QN2VANPWCKCTFGLTIQ5Q', 'name': 'projects/ext-datasets/operations/6HX4QN2VANPWCKCTFGLTIQ5Q'}\n","Grid 62\n","curr_year 2017\n","Saving data for Nadia 2017\n","Task Started {'state': 'READY', 'description': 'Nadia_62_2017', 'priority': 100, 'creation_timestamp_ms': 1762654889075, 'update_timestamp_ms': 1762654889075, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'J4J7OZVT7DZ7VIZ54AMQUUGM', 'name': 'projects/ext-datasets/operations/J4J7OZVT7DZ7VIZ54AMQUUGM'}\n","Grid 63\n","curr_year 2017\n","Saving data for Nadia 2017\n","Task Started {'state': 'READY', 'description': 'Nadia_63_2017', 'priority': 100, 'creation_timestamp_ms': 1762654898050, 'update_timestamp_ms': 1762654898050, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '6V6KGVQEOODRMADWTEC5VUIX', 'name': 'projects/ext-datasets/operations/6V6KGVQEOODRMADWTEC5VUIX'}\n","Grid 64\n","curr_year 2017\n","Saving data for Nadia 2017\n","Task Started {'state': 'READY', 'description': 'Nadia_64_2017', 'priority': 100, 'creation_timestamp_ms': 1762654907283, 'update_timestamp_ms': 1762654907283, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'PXHUD3F4RPLGPJOENXUU7QLH', 'name': 'projects/ext-datasets/operations/PXHUD3F4RPLGPJOENXUU7QLH'}\n","Grid 65\n","curr_year 2017\n","Saving data for Nadia 2017\n","Task Started {'state': 'READY', 'description': 'Nadia_65_2017', 'priority': 100, 'creation_timestamp_ms': 1762654913799, 'update_timestamp_ms': 1762654913799, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'X22HJFUCDZTOY3LEVAP475AG', 'name': 'projects/ext-datasets/operations/X22HJFUCDZTOY3LEVAP475AG'}\n","Grid 66\n","curr_year 2017\n","Saving data for Nadia 2017\n","Task Started {'state': 'READY', 'description': 'Nadia_66_2017', 'priority': 100, 'creation_timestamp_ms': 1762654918742, 'update_timestamp_ms': 1762654918742, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'KTJGUNDA4CFEUNAM56WYNUW5', 'name': 'projects/ext-datasets/operations/KTJGUNDA4CFEUNAM56WYNUW5'}\n","Grid 67\n","curr_year 2017\n","Saving data for Nadia 2017\n","Task Started {'state': 'READY', 'description': 'Nadia_67_2017', 'priority': 100, 'creation_timestamp_ms': 1762654925619, 'update_timestamp_ms': 1762654925619, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'H5NS234TDT6RSMEP4GIGW3FT', 'name': 'projects/ext-datasets/operations/H5NS234TDT6RSMEP4GIGW3FT'}\n","Grid 68\n","curr_year 2017\n","Saving data for Nadia 2017\n","Task Started {'state': 'READY', 'description': 'Nadia_68_2017', 'priority': 100, 'creation_timestamp_ms': 1762654931695, 'update_timestamp_ms': 1762654931695, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'CMXM2QRIPI5NQ46B6W3OZZ4E', 'name': 'projects/ext-datasets/operations/CMXM2QRIPI5NQ46B6W3OZZ4E'}\n","Grid 69\n","curr_year 2017\n","Saving data for Nadia 2017\n","Task Started {'state': 'READY', 'description': 'Nadia_69_2017', 'priority': 100, 'creation_timestamp_ms': 1762654937022, 'update_timestamp_ms': 1762654937022, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'DFCGXRSZ5FPTOVNFXDYRLDJJ', 'name': 'projects/ext-datasets/operations/DFCGXRSZ5FPTOVNFXDYRLDJJ'}\n","Grid 70\n","curr_year 2017\n","Saving data for Nadia 2017\n","Task Started {'state': 'READY', 'description': 'Nadia_70_2017', 'priority': 100, 'creation_timestamp_ms': 1762654943181, 'update_timestamp_ms': 1762654943181, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'VOCVCP5TOGPBCZ6DVT5MUCPL', 'name': 'projects/ext-datasets/operations/VOCVCP5TOGPBCZ6DVT5MUCPL'}\n","Grid 71\n","curr_year 2017\n","Saving data for Nadia 2017\n","Task Started {'state': 'READY', 'description': 'Nadia_71_2017', 'priority': 100, 'creation_timestamp_ms': 1762654951856, 'update_timestamp_ms': 1762654951856, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'ZAI6CKVCWKYXPUH56URUNSEM', 'name': 'projects/ext-datasets/operations/ZAI6CKVCWKYXPUH56URUNSEM'}\n","5405.53115773201\n","Year 2017, District 22: North 24 Parganas, grids: 105\n","Grid 0\n","curr_year 2017\n","Saving data for North 24 Parganas 2017\n","Task Started {'state': 'READY', 'description': 'North 24 Parganas_0_2017', 'priority': 100, 'creation_timestamp_ms': 1762654976848, 'update_timestamp_ms': 1762654976848, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'IX3YTELXICHR7WVK2Z62N5ML', 'name': 'projects/ext-datasets/operations/IX3YTELXICHR7WVK2Z62N5ML'}\n","Grid 1\n","curr_year 2017\n","Saving data for North 24 Parganas 2017\n","Task Started {'state': 'READY', 'description': 'North 24 Parganas_1_2017', 'priority': 100, 'creation_timestamp_ms': 1762654988635, 'update_timestamp_ms': 1762654988635, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'HQTZLQNDDVIFV2COJGSYYUC3', 'name': 'projects/ext-datasets/operations/HQTZLQNDDVIFV2COJGSYYUC3'}\n","Grid 2\n","curr_year 2017\n","Saving data for North 24 Parganas 2017\n","Task Started {'state': 'READY', 'description': 'North 24 Parganas_2_2017', 'priority': 100, 'creation_timestamp_ms': 1762655000432, 'update_timestamp_ms': 1762655000432, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'HJEHLVGA7GCO2NXDI6UC6C2F', 'name': 'projects/ext-datasets/operations/HJEHLVGA7GCO2NXDI6UC6C2F'}\n","Grid 3\n","curr_year 2017\n","Saving data for North 24 Parganas 2017\n","Task Started {'state': 'READY', 'description': 'North 24 Parganas_3_2017', 'priority': 100, 'creation_timestamp_ms': 1762655012757, 'update_timestamp_ms': 1762655012757, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '57SZOWMBEBJ5WBYS4TKGECYJ', 'name': 'projects/ext-datasets/operations/57SZOWMBEBJ5WBYS4TKGECYJ'}\n","Grid 4\n","curr_year 2017\n","Saving data for North 24 Parganas 2017\n","Task Started {'state': 'READY', 'description': 'North 24 Parganas_4_2017', 'priority': 100, 'creation_timestamp_ms': 1762655030436, 'update_timestamp_ms': 1762655030436, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'DZL2TEFZSV4PTLOAACAPA7GC', 'name': 'projects/ext-datasets/operations/DZL2TEFZSV4PTLOAACAPA7GC'}\n","Grid 5\n","curr_year 2017\n","Saving data for North 24 Parganas 2017\n","Task Started {'state': 'READY', 'description': 'North 24 Parganas_5_2017', 'priority': 100, 'creation_timestamp_ms': 1762655044543, 'update_timestamp_ms': 1762655044543, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'UOHJY4563SXEXLQESUBMHGF7', 'name': 'projects/ext-datasets/operations/UOHJY4563SXEXLQESUBMHGF7'}\n","Grid 6\n","curr_year 2017\n","Saving data for North 24 Parganas 2017\n","Task Started {'state': 'READY', 'description': 'North 24 Parganas_6_2017', 'priority': 100, 'creation_timestamp_ms': 1762655056942, 'update_timestamp_ms': 1762655056942, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'QFUSNJ4LZ7Y4VI2AL4NUIF65', 'name': 'projects/ext-datasets/operations/QFUSNJ4LZ7Y4VI2AL4NUIF65'}\n","Grid 7\n","curr_year 2017\n","Saving data for North 24 Parganas 2017\n","Task Started {'state': 'READY', 'description': 'North 24 Parganas_7_2017', 'priority': 100, 'creation_timestamp_ms': 1762655076261, 'update_timestamp_ms': 1762655076261, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '7SXDEJIRQIBCJRVREOB4JIQB', 'name': 'projects/ext-datasets/operations/7SXDEJIRQIBCJRVREOB4JIQB'}\n","Grid 8\n","curr_year 2017\n","Saving data for North 24 Parganas 2017\n","Task Started {'state': 'READY', 'description': 'North 24 Parganas_8_2017', 'priority': 100, 'creation_timestamp_ms': 1762655088635, 'update_timestamp_ms': 1762655088635, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'OU4EF7IU5VABENSGND35BQ7B', 'name': 'projects/ext-datasets/operations/OU4EF7IU5VABENSGND35BQ7B'}\n","Grid 9\n","curr_year 2017\n","Saving data for North 24 Parganas 2017\n","Task Started {'state': 'READY', 'description': 'North 24 Parganas_9_2017', 'priority': 100, 'creation_timestamp_ms': 1762655103067, 'update_timestamp_ms': 1762655103067, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '7SEPBYXLSKKQ2OZI2W3FYKKP', 'name': 'projects/ext-datasets/operations/7SEPBYXLSKKQ2OZI2W3FYKKP'}\n","Grid 10\n","curr_year 2017\n","Saving data for North 24 Parganas 2017\n","Task Started {'state': 'READY', 'description': 'North 24 Parganas_10_2017', 'priority': 100, 'creation_timestamp_ms': 1762655119325, 'update_timestamp_ms': 1762655119325, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'FGJALR34NPODVL3QEPSEFAB3', 'name': 'projects/ext-datasets/operations/FGJALR34NPODVL3QEPSEFAB3'}\n","Grid 11\n","curr_year 2017\n","Saving data for North 24 Parganas 2017\n","Task Started {'state': 'READY', 'description': 'North 24 Parganas_11_2017', 'priority': 100, 'creation_timestamp_ms': 1762655132956, 'update_timestamp_ms': 1762655132956, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'WUUIQAY4G7HDHHXDCIIU53DT', 'name': 'projects/ext-datasets/operations/WUUIQAY4G7HDHHXDCIIU53DT'}\n","Grid 12\n","curr_year 2017\n","Saving data for North 24 Parganas 2017\n","Task Started {'state': 'READY', 'description': 'North 24 Parganas_12_2017', 'priority': 100, 'creation_timestamp_ms': 1762655143399, 'update_timestamp_ms': 1762655143399, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '47AR4NXM5ACLYTVF2MOUQGS6', 'name': 'projects/ext-datasets/operations/47AR4NXM5ACLYTVF2MOUQGS6'}\n","Grid 13\n","curr_year 2017\n","Saving data for North 24 Parganas 2017\n","Task Started {'state': 'READY', 'description': 'North 24 Parganas_13_2017', 'priority': 100, 'creation_timestamp_ms': 1762655161623, 'update_timestamp_ms': 1762655161623, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'NXL5YFKZPM7J4UBKCZPMRRTI', 'name': 'projects/ext-datasets/operations/NXL5YFKZPM7J4UBKCZPMRRTI'}\n","Grid 14\n","curr_year 2017\n","Saving data for North 24 Parganas 2017\n","Task Started {'state': 'READY', 'description': 'North 24 Parganas_14_2017', 'priority': 100, 'creation_timestamp_ms': 1762655168524, 'update_timestamp_ms': 1762655168524, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'IH44RSFCI7PYZ6ESQIS6DRGK', 'name': 'projects/ext-datasets/operations/IH44RSFCI7PYZ6ESQIS6DRGK'}\n","Grid 15\n","curr_year 2017\n","Saving data for North 24 Parganas 2017\n","Task Started {'state': 'READY', 'description': 'North 24 Parganas_15_2017', 'priority': 100, 'creation_timestamp_ms': 1762655180629, 'update_timestamp_ms': 1762655180629, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'VXJC2T6FBLXSZEHJVAG57HOY', 'name': 'projects/ext-datasets/operations/VXJC2T6FBLXSZEHJVAG57HOY'}\n","Grid 16\n","curr_year 2017\n","Saving data for North 24 Parganas 2017\n","Task Started {'state': 'READY', 'description': 'North 24 Parganas_16_2017', 'priority': 100, 'creation_timestamp_ms': 1762655190174, 'update_timestamp_ms': 1762655190174, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'NKDBBM5DZ2TNHTM7UFKR3A46', 'name': 'projects/ext-datasets/operations/NKDBBM5DZ2TNHTM7UFKR3A46'}\n","Grid 17\n","curr_year 2017\n","Saving data for North 24 Parganas 2017\n","Task Started {'state': 'READY', 'description': 'North 24 Parganas_17_2017', 'priority': 100, 'creation_timestamp_ms': 1762655202483, 'update_timestamp_ms': 1762655202483, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'CB5ERTFD4JPIXUEVYR34YVRY', 'name': 'projects/ext-datasets/operations/CB5ERTFD4JPIXUEVYR34YVRY'}\n","Grid 18\n","curr_year 2017\n","Saving data for North 24 Parganas 2017\n","Task Started {'state': 'READY', 'description': 'North 24 Parganas_18_2017', 'priority': 100, 'creation_timestamp_ms': 1762655212750, 'update_timestamp_ms': 1762655212750, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'JV3HE5IDT23QL2IY7CR73QIM', 'name': 'projects/ext-datasets/operations/JV3HE5IDT23QL2IY7CR73QIM'}\n","Grid 19\n","curr_year 2017\n","Saving data for North 24 Parganas 2017\n","Task Started {'state': 'READY', 'description': 'North 24 Parganas_19_2017', 'priority': 100, 'creation_timestamp_ms': 1762655225852, 'update_timestamp_ms': 1762655225852, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'KT2OPPZXMCHK7LARGF5A3I43', 'name': 'projects/ext-datasets/operations/KT2OPPZXMCHK7LARGF5A3I43'}\n","Grid 20\n","curr_year 2017\n","Saving data for North 24 Parganas 2017\n","Task Started {'state': 'READY', 'description': 'North 24 Parganas_20_2017', 'priority': 100, 'creation_timestamp_ms': 1762655244130, 'update_timestamp_ms': 1762655244130, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'DWZ36N3TWAIMRRFLBZUGPMLT', 'name': 'projects/ext-datasets/operations/DWZ36N3TWAIMRRFLBZUGPMLT'}\n","Grid 21\n","curr_year 2017\n","Saving data for North 24 Parganas 2017\n","Task Started {'state': 'READY', 'description': 'North 24 Parganas_21_2017', 'priority': 100, 'creation_timestamp_ms': 1762655256212, 'update_timestamp_ms': 1762655256212, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '4GRPMKLZNUQ6PYKCJWRJE4LA', 'name': 'projects/ext-datasets/operations/4GRPMKLZNUQ6PYKCJWRJE4LA'}\n","Grid 22\n","curr_year 2017\n","Saving data for North 24 Parganas 2017\n","Task Started {'state': 'READY', 'description': 'North 24 Parganas_22_2017', 'priority': 100, 'creation_timestamp_ms': 1762655270216, 'update_timestamp_ms': 1762655270216, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'O6OVG3P3SUVO3Y5CZNCTZAVO', 'name': 'projects/ext-datasets/operations/O6OVG3P3SUVO3Y5CZNCTZAVO'}\n","Grid 23\n","curr_year 2017\n","Saving data for North 24 Parganas 2017\n","Task Started {'state': 'READY', 'description': 'North 24 Parganas_23_2017', 'priority': 100, 'creation_timestamp_ms': 1762655281646, 'update_timestamp_ms': 1762655281646, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '37IZXH6ORMCC75QDZWKBG22S', 'name': 'projects/ext-datasets/operations/37IZXH6ORMCC75QDZWKBG22S'}\n","Grid 24\n","curr_year 2017\n","Saving data for North 24 Parganas 2017\n","Task Started {'state': 'READY', 'description': 'North 24 Parganas_24_2017', 'priority': 100, 'creation_timestamp_ms': 1762655295759, 'update_timestamp_ms': 1762655295759, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'W6BZHPC5DVKLUT4AUFYFQG3G', 'name': 'projects/ext-datasets/operations/W6BZHPC5DVKLUT4AUFYFQG3G'}\n","Grid 25\n","curr_year 2017\n","Saving data for North 24 Parganas 2017\n","Task Started {'state': 'READY', 'description': 'North 24 Parganas_25_2017', 'priority': 100, 'creation_timestamp_ms': 1762655307696, 'update_timestamp_ms': 1762655307696, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'E32DF6SECLI4JF2Q62UKBIEZ', 'name': 'projects/ext-datasets/operations/E32DF6SECLI4JF2Q62UKBIEZ'}\n","Grid 26\n","curr_year 2017\n","Saving data for North 24 Parganas 2017\n","Task Started {'state': 'READY', 'description': 'North 24 Parganas_26_2017', 'priority': 100, 'creation_timestamp_ms': 1762655315987, 'update_timestamp_ms': 1762655315987, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'A7BTXPTBVCHH5FJADNUYVJQO', 'name': 'projects/ext-datasets/operations/A7BTXPTBVCHH5FJADNUYVJQO'}\n","Grid 27\n","curr_year 2017\n","Saving data for North 24 Parganas 2017\n","Task Started {'state': 'READY', 'description': 'North 24 Parganas_27_2017', 'priority': 100, 'creation_timestamp_ms': 1762655327366, 'update_timestamp_ms': 1762655327366, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'BHO6MBWKMQOXTJQUOQJWQ5F6', 'name': 'projects/ext-datasets/operations/BHO6MBWKMQOXTJQUOQJWQ5F6'}\n","Grid 28\n","curr_year 2017\n","Saving data for North 24 Parganas 2017\n","Task Started {'state': 'READY', 'description': 'North 24 Parganas_28_2017', 'priority': 100, 'creation_timestamp_ms': 1762655342826, 'update_timestamp_ms': 1762655342826, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'KRNTPQEAXGRCALHR6HC5KKDM', 'name': 'projects/ext-datasets/operations/KRNTPQEAXGRCALHR6HC5KKDM'}\n","Grid 29\n","curr_year 2017\n","Saving data for North 24 Parganas 2017\n","Task Started {'state': 'READY', 'description': 'North 24 Parganas_29_2017', 'priority': 100, 'creation_timestamp_ms': 1762655356008, 'update_timestamp_ms': 1762655356008, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'JPU5JNDCC6IUNL3KNOGALPJY', 'name': 'projects/ext-datasets/operations/JPU5JNDCC6IUNL3KNOGALPJY'}\n","Grid 30\n","curr_year 2017\n","Saving data for North 24 Parganas 2017\n","Task Started {'state': 'READY', 'description': 'North 24 Parganas_30_2017', 'priority': 100, 'creation_timestamp_ms': 1762655367045, 'update_timestamp_ms': 1762655367045, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'OHHR6AWOAWANUTFWOYT72LHL', 'name': 'projects/ext-datasets/operations/OHHR6AWOAWANUTFWOYT72LHL'}\n","Grid 31\n","curr_year 2017\n","Saving data for North 24 Parganas 2017\n","Task Started {'state': 'READY', 'description': 'North 24 Parganas_31_2017', 'priority': 100, 'creation_timestamp_ms': 1762655379618, 'update_timestamp_ms': 1762655379618, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'XLTHQ7QTAMO3MUJBHCPVBQCI', 'name': 'projects/ext-datasets/operations/XLTHQ7QTAMO3MUJBHCPVBQCI'}\n","Grid 32\n","curr_year 2017\n","Saving data for North 24 Parganas 2017\n","Task Started {'state': 'READY', 'description': 'North 24 Parganas_32_2017', 'priority': 100, 'creation_timestamp_ms': 1762655391240, 'update_timestamp_ms': 1762655391240, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'X6ZGIXYUNNVJFBM5LCJGVCM7', 'name': 'projects/ext-datasets/operations/X6ZGIXYUNNVJFBM5LCJGVCM7'}\n","Grid 33\n","curr_year 2017\n","Saving data for North 24 Parganas 2017\n","Task Started {'state': 'READY', 'description': 'North 24 Parganas_33_2017', 'priority': 100, 'creation_timestamp_ms': 1762655404979, 'update_timestamp_ms': 1762655404979, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'OPFFGITEIHWSSTGXUJIJSVOB', 'name': 'projects/ext-datasets/operations/OPFFGITEIHWSSTGXUJIJSVOB'}\n","Grid 34\n","curr_year 2017\n","Saving data for North 24 Parganas 2017\n","Task Started {'state': 'READY', 'description': 'North 24 Parganas_34_2017', 'priority': 100, 'creation_timestamp_ms': 1762655416831, 'update_timestamp_ms': 1762655416831, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'TMRMQRWM4NXFZLHUGQTNHPGG', 'name': 'projects/ext-datasets/operations/TMRMQRWM4NXFZLHUGQTNHPGG'}\n","Grid 35\n","curr_year 2017\n","Saving data for North 24 Parganas 2017\n","Task Started {'state': 'READY', 'description': 'North 24 Parganas_35_2017', 'priority': 100, 'creation_timestamp_ms': 1762655427035, 'update_timestamp_ms': 1762655427035, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '64CUDXKEVKXTUB2NKGA7IHHO', 'name': 'projects/ext-datasets/operations/64CUDXKEVKXTUB2NKGA7IHHO'}\n","Grid 36\n","curr_year 2017\n","Saving data for North 24 Parganas 2017\n","Task Started {'state': 'READY', 'description': 'North 24 Parganas_36_2017', 'priority': 100, 'creation_timestamp_ms': 1762655436251, 'update_timestamp_ms': 1762655436251, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'A6PB2MTGI7FROARAEHN7R4HT', 'name': 'projects/ext-datasets/operations/A6PB2MTGI7FROARAEHN7R4HT'}\n","Grid 37\n","curr_year 2017\n","Saving data for North 24 Parganas 2017\n","Task Started {'state': 'READY', 'description': 'North 24 Parganas_37_2017', 'priority': 100, 'creation_timestamp_ms': 1762655449694, 'update_timestamp_ms': 1762655449694, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'BO5O4Y5JK6ZRCILR5PNWHPAE', 'name': 'projects/ext-datasets/operations/BO5O4Y5JK6ZRCILR5PNWHPAE'}\n","Grid 38\n","curr_year 2017\n","Saving data for North 24 Parganas 2017\n","Task Started {'state': 'READY', 'description': 'North 24 Parganas_38_2017', 'priority': 100, 'creation_timestamp_ms': 1762655461029, 'update_timestamp_ms': 1762655461029, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'KQU6WKD25NUHN6SW7IMX3LVV', 'name': 'projects/ext-datasets/operations/KQU6WKD25NUHN6SW7IMX3LVV'}\n","Grid 39\n","curr_year 2017\n","Saving data for North 24 Parganas 2017\n","Task Started {'state': 'READY', 'description': 'North 24 Parganas_39_2017', 'priority': 100, 'creation_timestamp_ms': 1762655474265, 'update_timestamp_ms': 1762655474265, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'NKEINJ2WOJDUUXP72THAERTP', 'name': 'projects/ext-datasets/operations/NKEINJ2WOJDUUXP72THAERTP'}\n","Grid 40\n","curr_year 2017\n","Saving data for North 24 Parganas 2017\n","Task Started {'state': 'READY', 'description': 'North 24 Parganas_40_2017', 'priority': 100, 'creation_timestamp_ms': 1762655487921, 'update_timestamp_ms': 1762655487921, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'LTCKVS7DKSEAXJEXFW6U7UN4', 'name': 'projects/ext-datasets/operations/LTCKVS7DKSEAXJEXFW6U7UN4'}\n","Grid 41\n","curr_year 2017\n","Saving data for North 24 Parganas 2017\n","Task Started {'state': 'READY', 'description': 'North 24 Parganas_41_2017', 'priority': 100, 'creation_timestamp_ms': 1762655497459, 'update_timestamp_ms': 1762655497459, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'PQNPYVU7YWNBXVYX77A62EVL', 'name': 'projects/ext-datasets/operations/PQNPYVU7YWNBXVYX77A62EVL'}\n","Grid 42\n","curr_year 2017\n","Saving data for North 24 Parganas 2017\n","Task Started {'state': 'READY', 'description': 'North 24 Parganas_42_2017', 'priority': 100, 'creation_timestamp_ms': 1762655508634, 'update_timestamp_ms': 1762655508634, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'RVG2POCLM7KQU5Q26DD3ZWYM', 'name': 'projects/ext-datasets/operations/RVG2POCLM7KQU5Q26DD3ZWYM'}\n","Grid 43\n","curr_year 2017\n","Saving data for North 24 Parganas 2017\n","Task Started {'state': 'READY', 'description': 'North 24 Parganas_43_2017', 'priority': 100, 'creation_timestamp_ms': 1762655517217, 'update_timestamp_ms': 1762655517217, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'H45J2KL2RYHLAA6E5QAEEFC3', 'name': 'projects/ext-datasets/operations/H45J2KL2RYHLAA6E5QAEEFC3'}\n","Grid 44\n","curr_year 2017\n","Saving data for North 24 Parganas 2017\n","Task Started {'state': 'READY', 'description': 'North 24 Parganas_44_2017', 'priority': 100, 'creation_timestamp_ms': 1762655523539, 'update_timestamp_ms': 1762655523539, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'QGNDTXX66NOQCT5RLTDNBKF7', 'name': 'projects/ext-datasets/operations/QGNDTXX66NOQCT5RLTDNBKF7'}\n","Grid 45\n","curr_year 2017\n","Saving data for North 24 Parganas 2017\n","Task Started {'state': 'READY', 'description': 'North 24 Parganas_45_2017', 'priority': 100, 'creation_timestamp_ms': 1762655529939, 'update_timestamp_ms': 1762655529939, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'R4GA47JEOWN7CMGCXYPM5ME5', 'name': 'projects/ext-datasets/operations/R4GA47JEOWN7CMGCXYPM5ME5'}\n","Grid 46\n","curr_year 2017\n","Saving data for North 24 Parganas 2017\n","Task Started {'state': 'READY', 'description': 'North 24 Parganas_46_2017', 'priority': 100, 'creation_timestamp_ms': 1762655543210, 'update_timestamp_ms': 1762655543210, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'AGVLEVA4ZYWNF3F75BY3FKIP', 'name': 'projects/ext-datasets/operations/AGVLEVA4ZYWNF3F75BY3FKIP'}\n","Grid 47\n","curr_year 2017\n","Saving data for North 24 Parganas 2017\n","Task Started {'state': 'READY', 'description': 'North 24 Parganas_47_2017', 'priority': 100, 'creation_timestamp_ms': 1762655549662, 'update_timestamp_ms': 1762655549662, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'SOYKXSQHASSQCB5M5YJSYSHL', 'name': 'projects/ext-datasets/operations/SOYKXSQHASSQCB5M5YJSYSHL'}\n","Grid 48\n","curr_year 2017\n","Saving data for North 24 Parganas 2017\n","Task Started {'state': 'READY', 'description': 'North 24 Parganas_48_2017', 'priority': 100, 'creation_timestamp_ms': 1762655563350, 'update_timestamp_ms': 1762655563350, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'O7FMO7H5WC5XU3SPIZCX6NZE', 'name': 'projects/ext-datasets/operations/O7FMO7H5WC5XU3SPIZCX6NZE'}\n","Grid 49\n","curr_year 2017\n","Saving data for North 24 Parganas 2017\n","Task Started {'state': 'READY', 'description': 'North 24 Parganas_49_2017', 'priority': 100, 'creation_timestamp_ms': 1762655574655, 'update_timestamp_ms': 1762655574655, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'PHQDH6UTU65QV6BOVIH26CJB', 'name': 'projects/ext-datasets/operations/PHQDH6UTU65QV6BOVIH26CJB'}\n","Grid 50\n","curr_year 2017\n","Saving data for North 24 Parganas 2017\n","Task Started {'state': 'READY', 'description': 'North 24 Parganas_50_2017', 'priority': 100, 'creation_timestamp_ms': 1762655585367, 'update_timestamp_ms': 1762655585367, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'PKHHLPYXRAKWBVUEDGKPIAVY', 'name': 'projects/ext-datasets/operations/PKHHLPYXRAKWBVUEDGKPIAVY'}\n","Grid 51\n","curr_year 2017\n","Saving data for North 24 Parganas 2017\n","Task Started {'state': 'READY', 'description': 'North 24 Parganas_51_2017', 'priority': 100, 'creation_timestamp_ms': 1762655598013, 'update_timestamp_ms': 1762655598013, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'FCWDWGOBTXXEL3FZL66BXJBJ', 'name': 'projects/ext-datasets/operations/FCWDWGOBTXXEL3FZL66BXJBJ'}\n","Grid 52\n","curr_year 2017\n","Saving data for North 24 Parganas 2017\n","Task Started {'state': 'READY', 'description': 'North 24 Parganas_52_2017', 'priority': 100, 'creation_timestamp_ms': 1762655609770, 'update_timestamp_ms': 1762655609770, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'WLQ7KIFF54BPQYOJHUNZJYTP', 'name': 'projects/ext-datasets/operations/WLQ7KIFF54BPQYOJHUNZJYTP'}\n","Grid 53\n","curr_year 2017\n","Saving data for North 24 Parganas 2017\n","Task Started {'state': 'READY', 'description': 'North 24 Parganas_53_2017', 'priority': 100, 'creation_timestamp_ms': 1762655620410, 'update_timestamp_ms': 1762655620410, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'EWPX3KHW6CS2VJHRW7HCU64U', 'name': 'projects/ext-datasets/operations/EWPX3KHW6CS2VJHRW7HCU64U'}\n","Grid 54\n","curr_year 2017\n","Saving data for North 24 Parganas 2017\n","Task Started {'state': 'READY', 'description': 'North 24 Parganas_54_2017', 'priority': 100, 'creation_timestamp_ms': 1762655633339, 'update_timestamp_ms': 1762655633339, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'VND6DUIIUFA4UDNDC55IMNS6', 'name': 'projects/ext-datasets/operations/VND6DUIIUFA4UDNDC55IMNS6'}\n","Grid 55\n","curr_year 2017\n","Saving data for North 24 Parganas 2017\n","Task Started {'state': 'READY', 'description': 'North 24 Parganas_55_2017', 'priority': 100, 'creation_timestamp_ms': 1762655645576, 'update_timestamp_ms': 1762655645576, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '4YEKEDVHF2G4OKN7UXRWGBYE', 'name': 'projects/ext-datasets/operations/4YEKEDVHF2G4OKN7UXRWGBYE'}\n","Grid 56\n","curr_year 2017\n","Saving data for North 24 Parganas 2017\n","Task Started {'state': 'READY', 'description': 'North 24 Parganas_56_2017', 'priority': 100, 'creation_timestamp_ms': 1762655658201, 'update_timestamp_ms': 1762655658201, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'QN5AMLTUHMY6AHF4JWMCG3L3', 'name': 'projects/ext-datasets/operations/QN5AMLTUHMY6AHF4JWMCG3L3'}\n","Grid 57\n","curr_year 2017\n","Saving data for North 24 Parganas 2017\n","Task Started {'state': 'READY', 'description': 'North 24 Parganas_57_2017', 'priority': 100, 'creation_timestamp_ms': 1762655667677, 'update_timestamp_ms': 1762655667677, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'HHME5LVG7V5RETPFCIIWWUCU', 'name': 'projects/ext-datasets/operations/HHME5LVG7V5RETPFCIIWWUCU'}\n","Grid 58\n","curr_year 2017\n","Saving data for North 24 Parganas 2017\n","Task Started {'state': 'READY', 'description': 'North 24 Parganas_58_2017', 'priority': 100, 'creation_timestamp_ms': 1762655678161, 'update_timestamp_ms': 1762655678161, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'ZYG55LEM3YLQ2WJK7FTEP6YV', 'name': 'projects/ext-datasets/operations/ZYG55LEM3YLQ2WJK7FTEP6YV'}\n","Grid 59\n","curr_year 2017\n","Saving data for North 24 Parganas 2017\n","Task Started {'state': 'READY', 'description': 'North 24 Parganas_59_2017', 'priority': 100, 'creation_timestamp_ms': 1762655690215, 'update_timestamp_ms': 1762655690215, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'KWN4WCPDRQFPJSNST622DE4N', 'name': 'projects/ext-datasets/operations/KWN4WCPDRQFPJSNST622DE4N'}\n","Grid 60\n","curr_year 2017\n","Saving data for North 24 Parganas 2017\n","Task Started {'state': 'READY', 'description': 'North 24 Parganas_60_2017', 'priority': 100, 'creation_timestamp_ms': 1762655696093, 'update_timestamp_ms': 1762655696093, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '5S5O6LIB4YQQMFOAUBQHUSRP', 'name': 'projects/ext-datasets/operations/5S5O6LIB4YQQMFOAUBQHUSRP'}\n","Grid 61\n","curr_year 2017\n","Saving data for North 24 Parganas 2017\n","Task Started {'state': 'READY', 'description': 'North 24 Parganas_61_2017', 'priority': 100, 'creation_timestamp_ms': 1762655708255, 'update_timestamp_ms': 1762655708255, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'AIM3ISYMBBC5AGP4NYSX5V3X', 'name': 'projects/ext-datasets/operations/AIM3ISYMBBC5AGP4NYSX5V3X'}\n","Grid 62\n","curr_year 2017\n","Saving data for North 24 Parganas 2017\n","Task Started {'state': 'READY', 'description': 'North 24 Parganas_62_2017', 'priority': 100, 'creation_timestamp_ms': 1762655718928, 'update_timestamp_ms': 1762655718928, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'PC3IQNDBADK6WHIXMUTI4SVS', 'name': 'projects/ext-datasets/operations/PC3IQNDBADK6WHIXMUTI4SVS'}\n","Grid 63\n","curr_year 2017\n","Saving data for North 24 Parganas 2017\n","Task Started {'state': 'READY', 'description': 'North 24 Parganas_63_2017', 'priority': 100, 'creation_timestamp_ms': 1762655725111, 'update_timestamp_ms': 1762655725111, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '7IL4CBTLG5ESCTKBF5ZAG5HX', 'name': 'projects/ext-datasets/operations/7IL4CBTLG5ESCTKBF5ZAG5HX'}\n","Grid 64\n","curr_year 2017\n","Saving data for North 24 Parganas 2017\n","Task Started {'state': 'READY', 'description': 'North 24 Parganas_64_2017', 'priority': 100, 'creation_timestamp_ms': 1762655734244, 'update_timestamp_ms': 1762655734244, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'VTPIDHJ7PU6XLEDBASD6OKHO', 'name': 'projects/ext-datasets/operations/VTPIDHJ7PU6XLEDBASD6OKHO'}\n","Grid 65\n","curr_year 2017\n","Saving data for North 24 Parganas 2017\n","Task Started {'state': 'READY', 'description': 'North 24 Parganas_65_2017', 'priority': 100, 'creation_timestamp_ms': 1762655747051, 'update_timestamp_ms': 1762655747051, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '73M47M7S64CCFJLMXFSAIUSP', 'name': 'projects/ext-datasets/operations/73M47M7S64CCFJLMXFSAIUSP'}\n","Grid 66\n","curr_year 2017\n","Saving data for North 24 Parganas 2017\n","Task Started {'state': 'READY', 'description': 'North 24 Parganas_66_2017', 'priority': 100, 'creation_timestamp_ms': 1762655758633, 'update_timestamp_ms': 1762655758633, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '36SO62NUWDUPMIXGCYEAZF36', 'name': 'projects/ext-datasets/operations/36SO62NUWDUPMIXGCYEAZF36'}\n","Grid 67\n","curr_year 2017\n","Saving data for North 24 Parganas 2017\n","Task Started {'state': 'READY', 'description': 'North 24 Parganas_67_2017', 'priority': 100, 'creation_timestamp_ms': 1762655767427, 'update_timestamp_ms': 1762655767427, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'PTOMVNQSCULA2NAPAX4A7LUM', 'name': 'projects/ext-datasets/operations/PTOMVNQSCULA2NAPAX4A7LUM'}\n","Grid 68\n","curr_year 2017\n","Saving data for North 24 Parganas 2017\n","Task Started {'state': 'READY', 'description': 'North 24 Parganas_68_2017', 'priority': 100, 'creation_timestamp_ms': 1762655781290, 'update_timestamp_ms': 1762655781290, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'LTKXVT2D2W6N2A52D6NT6HO5', 'name': 'projects/ext-datasets/operations/LTKXVT2D2W6N2A52D6NT6HO5'}\n","Grid 69\n","curr_year 2017\n","Saving data for North 24 Parganas 2017\n","Task Started {'state': 'READY', 'description': 'North 24 Parganas_69_2017', 'priority': 100, 'creation_timestamp_ms': 1762655794396, 'update_timestamp_ms': 1762655794396, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'R7O5Q3QCBUHDD3X7FEALVVGK', 'name': 'projects/ext-datasets/operations/R7O5Q3QCBUHDD3X7FEALVVGK'}\n","Grid 70\n","curr_year 2017\n","Saving data for North 24 Parganas 2017\n","Task Started {'state': 'READY', 'description': 'North 24 Parganas_70_2017', 'priority': 100, 'creation_timestamp_ms': 1762655804805, 'update_timestamp_ms': 1762655804805, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'DCUUPEE7UGV5LWAKYAFFX62C', 'name': 'projects/ext-datasets/operations/DCUUPEE7UGV5LWAKYAFFX62C'}\n","Grid 71\n","curr_year 2017\n","Saving data for North 24 Parganas 2017\n","Task Started {'state': 'READY', 'description': 'North 24 Parganas_71_2017', 'priority': 100, 'creation_timestamp_ms': 1762655818999, 'update_timestamp_ms': 1762655818999, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'AC4VLPBOUELDZEOFKNYEAYSK', 'name': 'projects/ext-datasets/operations/AC4VLPBOUELDZEOFKNYEAYSK'}\n","Grid 72\n","curr_year 2017\n","Saving data for North 24 Parganas 2017\n","Task Started {'state': 'READY', 'description': 'North 24 Parganas_72_2017', 'priority': 100, 'creation_timestamp_ms': 1762655830408, 'update_timestamp_ms': 1762655830408, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'YIKMPSTGZRZ6XJVQZK5WPM6L', 'name': 'projects/ext-datasets/operations/YIKMPSTGZRZ6XJVQZK5WPM6L'}\n","Grid 73\n","curr_year 2017\n","Saving data for North 24 Parganas 2017\n","Task Started {'state': 'READY', 'description': 'North 24 Parganas_73_2017', 'priority': 100, 'creation_timestamp_ms': 1762655836444, 'update_timestamp_ms': 1762655836444, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '2IFISRQJO3D4IOMBI4WZPFL7', 'name': 'projects/ext-datasets/operations/2IFISRQJO3D4IOMBI4WZPFL7'}\n","Grid 74\n","curr_year 2017\n","Saving data for North 24 Parganas 2017\n","Task Started {'state': 'READY', 'description': 'North 24 Parganas_74_2017', 'priority': 100, 'creation_timestamp_ms': 1762655846537, 'update_timestamp_ms': 1762655846537, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'NPPKK3SL5QLEFNARGHEW4QSQ', 'name': 'projects/ext-datasets/operations/NPPKK3SL5QLEFNARGHEW4QSQ'}\n","Grid 75\n","curr_year 2017\n","Saving data for North 24 Parganas 2017\n","Task Started {'state': 'READY', 'description': 'North 24 Parganas_75_2017', 'priority': 100, 'creation_timestamp_ms': 1762655856602, 'update_timestamp_ms': 1762655856602, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'T4UVWYLPOGAKFAJE7DYRDZ76', 'name': 'projects/ext-datasets/operations/T4UVWYLPOGAKFAJE7DYRDZ76'}\n","Grid 76\n","curr_year 2017\n","Saving data for North 24 Parganas 2017\n","Task Started {'state': 'READY', 'description': 'North 24 Parganas_76_2017', 'priority': 100, 'creation_timestamp_ms': 1762655868124, 'update_timestamp_ms': 1762655868124, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'JEZ25FEEXJM4LMJ56J3DBR4B', 'name': 'projects/ext-datasets/operations/JEZ25FEEXJM4LMJ56J3DBR4B'}\n","Grid 77\n","curr_year 2017\n","Saving data for North 24 Parganas 2017\n","Task Started {'state': 'READY', 'description': 'North 24 Parganas_77_2017', 'priority': 100, 'creation_timestamp_ms': 1762655878153, 'update_timestamp_ms': 1762655878153, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'DBSDB7JA556AXJGHKT3N3MJ7', 'name': 'projects/ext-datasets/operations/DBSDB7JA556AXJGHKT3N3MJ7'}\n","Grid 78\n","curr_year 2017\n","Saving data for North 24 Parganas 2017\n","Task Started {'state': 'READY', 'description': 'North 24 Parganas_78_2017', 'priority': 100, 'creation_timestamp_ms': 1762655888430, 'update_timestamp_ms': 1762655888430, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'Z6WTJCMTQJ5QNDY6MJUJYK7I', 'name': 'projects/ext-datasets/operations/Z6WTJCMTQJ5QNDY6MJUJYK7I'}\n","Grid 79\n","curr_year 2017\n","Saving data for North 24 Parganas 2017\n","Task Started {'state': 'READY', 'description': 'North 24 Parganas_79_2017', 'priority': 100, 'creation_timestamp_ms': 1762655899704, 'update_timestamp_ms': 1762655899704, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'J7KMXLL6WPC6RR54WXNP67C2', 'name': 'projects/ext-datasets/operations/J7KMXLL6WPC6RR54WXNP67C2'}\n","Grid 80\n","curr_year 2017\n","Saving data for North 24 Parganas 2017\n","Task Started {'state': 'READY', 'description': 'North 24 Parganas_80_2017', 'priority': 100, 'creation_timestamp_ms': 1762655908193, 'update_timestamp_ms': 1762655908193, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '22ZVGIHCZPFFTKELHEAG4J2H', 'name': 'projects/ext-datasets/operations/22ZVGIHCZPFFTKELHEAG4J2H'}\n","Grid 81\n","curr_year 2017\n","Saving data for North 24 Parganas 2017\n","Task Started {'state': 'READY', 'description': 'North 24 Parganas_81_2017', 'priority': 100, 'creation_timestamp_ms': 1762655919120, 'update_timestamp_ms': 1762655919120, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'VDDSVPBRKZEQ2AGRUBCBZ5IQ', 'name': 'projects/ext-datasets/operations/VDDSVPBRKZEQ2AGRUBCBZ5IQ'}\n","Grid 82\n","curr_year 2017\n","Saving data for North 24 Parganas 2017\n","Task Started {'state': 'READY', 'description': 'North 24 Parganas_82_2017', 'priority': 100, 'creation_timestamp_ms': 1762655932454, 'update_timestamp_ms': 1762655932454, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'SUOKV2YMIZ2GRASZ2HGX3LO7', 'name': 'projects/ext-datasets/operations/SUOKV2YMIZ2GRASZ2HGX3LO7'}\n","Grid 83\n","curr_year 2017\n","Saving data for North 24 Parganas 2017\n","Task Started {'state': 'READY', 'description': 'North 24 Parganas_83_2017', 'priority': 100, 'creation_timestamp_ms': 1762655944385, 'update_timestamp_ms': 1762655944385, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'GRU4X6X53JPRGQQ2UMKYDMIQ', 'name': 'projects/ext-datasets/operations/GRU4X6X53JPRGQQ2UMKYDMIQ'}\n","Grid 84\n","curr_year 2017\n","Saving data for North 24 Parganas 2017\n","Task Started {'state': 'READY', 'description': 'North 24 Parganas_84_2017', 'priority': 100, 'creation_timestamp_ms': 1762655954682, 'update_timestamp_ms': 1762655954682, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'F5YOTQSJ64LU7DUZJAL46QLL', 'name': 'projects/ext-datasets/operations/F5YOTQSJ64LU7DUZJAL46QLL'}\n","Grid 85\n","curr_year 2017\n","Saving data for North 24 Parganas 2017\n","Task Started {'state': 'READY', 'description': 'North 24 Parganas_85_2017', 'priority': 100, 'creation_timestamp_ms': 1762655969185, 'update_timestamp_ms': 1762655969185, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'UWZQBZI76ZIRNEPITYPMK46P', 'name': 'projects/ext-datasets/operations/UWZQBZI76ZIRNEPITYPMK46P'}\n","Grid 86\n","curr_year 2017\n","Saving data for North 24 Parganas 2017\n","Task Started {'state': 'READY', 'description': 'North 24 Parganas_86_2017', 'priority': 100, 'creation_timestamp_ms': 1762655982255, 'update_timestamp_ms': 1762655982255, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'VMJKZLV2PVBXQQXL63SZKWVM', 'name': 'projects/ext-datasets/operations/VMJKZLV2PVBXQQXL63SZKWVM'}\n","Grid 87\n","curr_year 2017\n","Saving data for North 24 Parganas 2017\n","Task Started {'state': 'READY', 'description': 'North 24 Parganas_87_2017', 'priority': 100, 'creation_timestamp_ms': 1762655993638, 'update_timestamp_ms': 1762655993638, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'ED5VBUYOJL52KIN7QTE4LJKI', 'name': 'projects/ext-datasets/operations/ED5VBUYOJL52KIN7QTE4LJKI'}\n","Grid 88\n","curr_year 2017\n","Saving data for North 24 Parganas 2017\n","Task Started {'state': 'READY', 'description': 'North 24 Parganas_88_2017', 'priority': 100, 'creation_timestamp_ms': 1762656006534, 'update_timestamp_ms': 1762656006534, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'P3YUV5F7TM2ZJEWT5UKRVM2S', 'name': 'projects/ext-datasets/operations/P3YUV5F7TM2ZJEWT5UKRVM2S'}\n","Grid 89\n","curr_year 2017\n","Saving data for North 24 Parganas 2017\n","Task Started {'state': 'READY', 'description': 'North 24 Parganas_89_2017', 'priority': 100, 'creation_timestamp_ms': 1762656013201, 'update_timestamp_ms': 1762656013201, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '3PADHC3TZWN44XTCZSC7BTWU', 'name': 'projects/ext-datasets/operations/3PADHC3TZWN44XTCZSC7BTWU'}\n","Grid 90\n","curr_year 2017\n","Saving data for North 24 Parganas 2017\n","Task Started {'state': 'READY', 'description': 'North 24 Parganas_90_2017', 'priority': 100, 'creation_timestamp_ms': 1762656023237, 'update_timestamp_ms': 1762656023237, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'MG5W4B57RFQUUHP2JAPEJJQU', 'name': 'projects/ext-datasets/operations/MG5W4B57RFQUUHP2JAPEJJQU'}\n","Grid 91\n","curr_year 2017\n","Saving data for North 24 Parganas 2017\n","Task Started {'state': 'READY', 'description': 'North 24 Parganas_91_2017', 'priority': 100, 'creation_timestamp_ms': 1762656035260, 'update_timestamp_ms': 1762656035260, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'K3TUBH7I2NLEAW4UHUZOZZBQ', 'name': 'projects/ext-datasets/operations/K3TUBH7I2NLEAW4UHUZOZZBQ'}\n","Grid 92\n","curr_year 2017\n","Saving data for North 24 Parganas 2017\n","Task Started {'state': 'READY', 'description': 'North 24 Parganas_92_2017', 'priority': 100, 'creation_timestamp_ms': 1762656048547, 'update_timestamp_ms': 1762656048547, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'QSIW3MTO4K5FBZPUZ37HL3RB', 'name': 'projects/ext-datasets/operations/QSIW3MTO4K5FBZPUZ37HL3RB'}\n","Grid 93\n","curr_year 2017\n","Saving data for North 24 Parganas 2017\n","Task Started {'state': 'READY', 'description': 'North 24 Parganas_93_2017', 'priority': 100, 'creation_timestamp_ms': 1762656054558, 'update_timestamp_ms': 1762656054558, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'L242L32UERU36XZJUCVSPXBN', 'name': 'projects/ext-datasets/operations/L242L32UERU36XZJUCVSPXBN'}\n","Grid 94\n","curr_year 2017\n","Saving data for North 24 Parganas 2017\n","Task Started {'state': 'READY', 'description': 'North 24 Parganas_94_2017', 'priority': 100, 'creation_timestamp_ms': 1762656065988, 'update_timestamp_ms': 1762656065988, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'SSSQWNHQHBAXPYSYO5QNAIOS', 'name': 'projects/ext-datasets/operations/SSSQWNHQHBAXPYSYO5QNAIOS'}\n","Grid 95\n","curr_year 2017\n","Saving data for North 24 Parganas 2017\n","Task Started {'state': 'READY', 'description': 'North 24 Parganas_95_2017', 'priority': 100, 'creation_timestamp_ms': 1762656075268, 'update_timestamp_ms': 1762656075268, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '42SVQ22STRZEWQBL4CEBOEQH', 'name': 'projects/ext-datasets/operations/42SVQ22STRZEWQBL4CEBOEQH'}\n","Grid 96\n","curr_year 2017\n","Saving data for North 24 Parganas 2017\n","Task Started {'state': 'READY', 'description': 'North 24 Parganas_96_2017', 'priority': 100, 'creation_timestamp_ms': 1762656085224, 'update_timestamp_ms': 1762656085224, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'NQGQXCWPFKOTQWX3SJCW74XM', 'name': 'projects/ext-datasets/operations/NQGQXCWPFKOTQWX3SJCW74XM'}\n","Grid 97\n","curr_year 2017\n","Saving data for North 24 Parganas 2017\n","Task Started {'state': 'READY', 'description': 'North 24 Parganas_97_2017', 'priority': 100, 'creation_timestamp_ms': 1762656098910, 'update_timestamp_ms': 1762656098910, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'EKILBAPLUR6HIHYST2K4C776', 'name': 'projects/ext-datasets/operations/EKILBAPLUR6HIHYST2K4C776'}\n","Grid 98\n","curr_year 2017\n","Saving data for North 24 Parganas 2017\n","Task Started {'state': 'READY', 'description': 'North 24 Parganas_98_2017', 'priority': 100, 'creation_timestamp_ms': 1762656111954, 'update_timestamp_ms': 1762656111954, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'OLWXDYBZJH5AP6XBS6YF6S3M', 'name': 'projects/ext-datasets/operations/OLWXDYBZJH5AP6XBS6YF6S3M'}\n","Grid 99\n","curr_year 2017\n","Saving data for North 24 Parganas 2017\n","Task Started {'state': 'READY', 'description': 'North 24 Parganas_99_2017', 'priority': 100, 'creation_timestamp_ms': 1762656123282, 'update_timestamp_ms': 1762656123282, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'SJ5JB5XL45YMAX3KWSRF3YHG', 'name': 'projects/ext-datasets/operations/SJ5JB5XL45YMAX3KWSRF3YHG'}\n","Grid 100\n","curr_year 2017\n","Saving data for North 24 Parganas 2017\n","Task Started {'state': 'READY', 'description': 'North 24 Parganas_100_2017', 'priority': 100, 'creation_timestamp_ms': 1762656135162, 'update_timestamp_ms': 1762656135162, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'EOWGBJLP42XPBVYFSO7PEZTR', 'name': 'projects/ext-datasets/operations/EOWGBJLP42XPBVYFSO7PEZTR'}\n","Grid 101\n","curr_year 2017\n","Saving data for North 24 Parganas 2017\n","Task Started {'state': 'READY', 'description': 'North 24 Parganas_101_2017', 'priority': 100, 'creation_timestamp_ms': 1762656146769, 'update_timestamp_ms': 1762656146769, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'ZHUWTEUEEKVWUPJX2IPOKDLR', 'name': 'projects/ext-datasets/operations/ZHUWTEUEEKVWUPJX2IPOKDLR'}\n","Grid 102\n","curr_year 2017\n","Saving data for North 24 Parganas 2017\n","Task Started {'state': 'READY', 'description': 'North 24 Parganas_102_2017', 'priority': 100, 'creation_timestamp_ms': 1762656158297, 'update_timestamp_ms': 1762656158297, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'M6ULVNULWU4C2QTCCQWIACXT', 'name': 'projects/ext-datasets/operations/M6ULVNULWU4C2QTCCQWIACXT'}\n","Grid 103\n","curr_year 2017\n","Saving data for North 24 Parganas 2017\n","Task Started {'state': 'READY', 'description': 'North 24 Parganas_103_2017', 'priority': 100, 'creation_timestamp_ms': 1762656168478, 'update_timestamp_ms': 1762656168478, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'DQACBR4OLFAKEA73STZMIODJ', 'name': 'projects/ext-datasets/operations/DQACBR4OLFAKEA73STZMIODJ'}\n","Grid 104\n","curr_year 2017\n","Saving data for North 24 Parganas 2017\n","Task Started {'state': 'READY', 'description': 'North 24 Parganas_104_2017', 'priority': 100, 'creation_timestamp_ms': 1762656179056, 'update_timestamp_ms': 1762656179056, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'TGRBOCRF7CMNYEOPSAYYMBXB', 'name': 'projects/ext-datasets/operations/TGRBOCRF7CMNYEOPSAYYMBXB'}\n","6632.857327699661\n","Year 2017, District 23: Pashchim Medinipur, grids: 137\n","Grid 0\n","curr_year 2017\n","Saving data for Pashchim Medinipur 2017\n","Task Started {'state': 'READY', 'description': 'Pashchim Medinipur_0_2017', 'priority': 100, 'creation_timestamp_ms': 1762656191525, 'update_timestamp_ms': 1762656191525, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'N5IXXWLNZPAOUEC2LUF6GBFG', 'name': 'projects/ext-datasets/operations/N5IXXWLNZPAOUEC2LUF6GBFG'}\n","Grid 1\n","curr_year 2017\n","Saving data for Pashchim Medinipur 2017\n","Task Started {'state': 'READY', 'description': 'Pashchim Medinipur_1_2017', 'priority': 100, 'creation_timestamp_ms': 1762656199110, 'update_timestamp_ms': 1762656199110, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '3GPF6PQ4CNSVXY7Z7UELEKEC', 'name': 'projects/ext-datasets/operations/3GPF6PQ4CNSVXY7Z7UELEKEC'}\n","Grid 2\n","curr_year 2017\n","Saving data for Pashchim Medinipur 2017\n","Task Started {'state': 'READY', 'description': 'Pashchim Medinipur_2_2017', 'priority': 100, 'creation_timestamp_ms': 1762656205412, 'update_timestamp_ms': 1762656205412, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'KO7DGC6KXHXA7TENULOYJQOV', 'name': 'projects/ext-datasets/operations/KO7DGC6KXHXA7TENULOYJQOV'}\n","Grid 3\n","curr_year 2017\n","Saving data for Pashchim Medinipur 2017\n","Task Started {'state': 'READY', 'description': 'Pashchim Medinipur_3_2017', 'priority': 100, 'creation_timestamp_ms': 1762656211108, 'update_timestamp_ms': 1762656211108, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'GGFAAJUZAOI6J7MMCSS3Z2IN', 'name': 'projects/ext-datasets/operations/GGFAAJUZAOI6J7MMCSS3Z2IN'}\n","Grid 4\n","curr_year 2017\n","Saving data for Pashchim Medinipur 2017\n","Task Started {'state': 'READY', 'description': 'Pashchim Medinipur_4_2017', 'priority': 100, 'creation_timestamp_ms': 1762656219479, 'update_timestamp_ms': 1762656219479, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'WOVFKMXZRPBR2AHE6J4UYZWT', 'name': 'projects/ext-datasets/operations/WOVFKMXZRPBR2AHE6J4UYZWT'}\n","Grid 5\n","curr_year 2017\n","Saving data for Pashchim Medinipur 2017\n","Task Started {'state': 'READY', 'description': 'Pashchim Medinipur_5_2017', 'priority': 100, 'creation_timestamp_ms': 1762656226538, 'update_timestamp_ms': 1762656226538, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'A6LXPACANOCYOG5E3INVUFSX', 'name': 'projects/ext-datasets/operations/A6LXPACANOCYOG5E3INVUFSX'}\n","Grid 6\n","curr_year 2017\n","Saving data for Pashchim Medinipur 2017\n","Task Started {'state': 'READY', 'description': 'Pashchim Medinipur_6_2017', 'priority': 100, 'creation_timestamp_ms': 1762656233248, 'update_timestamp_ms': 1762656233248, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'GYKC34WL45AXUBSP3MLXTMAR', 'name': 'projects/ext-datasets/operations/GYKC34WL45AXUBSP3MLXTMAR'}\n","Grid 7\n","curr_year 2017\n","Saving data for Pashchim Medinipur 2017\n","Task Started {'state': 'READY', 'description': 'Pashchim Medinipur_7_2017', 'priority': 100, 'creation_timestamp_ms': 1762656241331, 'update_timestamp_ms': 1762656241331, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'KVIJ7Q56RFCYRAPI4PW2ZHCC', 'name': 'projects/ext-datasets/operations/KVIJ7Q56RFCYRAPI4PW2ZHCC'}\n","Grid 8\n","curr_year 2017\n","Saving data for Pashchim Medinipur 2017\n","Task Started {'state': 'READY', 'description': 'Pashchim Medinipur_8_2017', 'priority': 100, 'creation_timestamp_ms': 1762656248402, 'update_timestamp_ms': 1762656248402, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'X5SC6YCJTY5C5HFKL745JI6Z', 'name': 'projects/ext-datasets/operations/X5SC6YCJTY5C5HFKL745JI6Z'}\n","Grid 9\n","curr_year 2017\n","Saving data for Pashchim Medinipur 2017\n","Task Started {'state': 'READY', 'description': 'Pashchim Medinipur_9_2017', 'priority': 100, 'creation_timestamp_ms': 1762656257963, 'update_timestamp_ms': 1762656257963, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'O6PQHD2BGKHGAEO6QSI6P5GC', 'name': 'projects/ext-datasets/operations/O6PQHD2BGKHGAEO6QSI6P5GC'}\n","Grid 10\n","curr_year 2017\n","Saving data for Pashchim Medinipur 2017\n","Task Started {'state': 'READY', 'description': 'Pashchim Medinipur_10_2017', 'priority': 100, 'creation_timestamp_ms': 1762656261641, 'update_timestamp_ms': 1762656261641, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '524RUKBLGXLDC4A6WALRMP3G', 'name': 'projects/ext-datasets/operations/524RUKBLGXLDC4A6WALRMP3G'}\n","Grid 11\n","curr_year 2017\n","Saving data for Pashchim Medinipur 2017\n","Task Started {'state': 'READY', 'description': 'Pashchim Medinipur_11_2017', 'priority': 100, 'creation_timestamp_ms': 1762656271484, 'update_timestamp_ms': 1762656271484, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'YLU53JR2VIEKKHALFJZHDOGD', 'name': 'projects/ext-datasets/operations/YLU53JR2VIEKKHALFJZHDOGD'}\n","Grid 12\n","curr_year 2017\n","Saving data for Pashchim Medinipur 2017\n","Task Started {'state': 'READY', 'description': 'Pashchim Medinipur_12_2017', 'priority': 100, 'creation_timestamp_ms': 1762656276108, 'update_timestamp_ms': 1762656276108, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'MW2TQDKNT6SO7ENF26DIKV5M', 'name': 'projects/ext-datasets/operations/MW2TQDKNT6SO7ENF26DIKV5M'}\n","Grid 13\n","curr_year 2017\n","Saving data for Pashchim Medinipur 2017\n","Task Started {'state': 'READY', 'description': 'Pashchim Medinipur_13_2017', 'priority': 100, 'creation_timestamp_ms': 1762656280867, 'update_timestamp_ms': 1762656280867, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'RWFA2T4ICTSYJHAFZ235E2MU', 'name': 'projects/ext-datasets/operations/RWFA2T4ICTSYJHAFZ235E2MU'}\n","Grid 14\n","curr_year 2017\n","Saving data for Pashchim Medinipur 2017\n","Task Started {'state': 'READY', 'description': 'Pashchim Medinipur_14_2017', 'priority': 100, 'creation_timestamp_ms': 1762656288738, 'update_timestamp_ms': 1762656288738, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'XLDBQFW7US6GNAPO7XUL7XKV', 'name': 'projects/ext-datasets/operations/XLDBQFW7US6GNAPO7XUL7XKV'}\n","Grid 15\n","curr_year 2017\n","Saving data for Pashchim Medinipur 2017\n","Task Started {'state': 'READY', 'description': 'Pashchim Medinipur_15_2017', 'priority': 100, 'creation_timestamp_ms': 1762656292460, 'update_timestamp_ms': 1762656292460, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'MXV4MNAWY2S6RXIX5P4SC4H4', 'name': 'projects/ext-datasets/operations/MXV4MNAWY2S6RXIX5P4SC4H4'}\n","Grid 16\n","curr_year 2017\n","Saving data for Pashchim Medinipur 2017\n","Task Started {'state': 'READY', 'description': 'Pashchim Medinipur_16_2017', 'priority': 100, 'creation_timestamp_ms': 1762656296534, 'update_timestamp_ms': 1762656296534, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '5FVBS7U4SHMCGLG7LMCYWZCN', 'name': 'projects/ext-datasets/operations/5FVBS7U4SHMCGLG7LMCYWZCN'}\n","Grid 17\n","curr_year 2017\n","Saving data for Pashchim Medinipur 2017\n","Task Started {'state': 'READY', 'description': 'Pashchim Medinipur_17_2017', 'priority': 100, 'creation_timestamp_ms': 1762656300879, 'update_timestamp_ms': 1762656300879, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'FQEB4PWKHHMNKF2Q5FDWZSSK', 'name': 'projects/ext-datasets/operations/FQEB4PWKHHMNKF2Q5FDWZSSK'}\n","Grid 18\n","curr_year 2017\n","Saving data for Pashchim Medinipur 2017\n","Task Started {'state': 'READY', 'description': 'Pashchim Medinipur_18_2017', 'priority': 100, 'creation_timestamp_ms': 1762656309319, 'update_timestamp_ms': 1762656309319, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'GH4XP7S2XMN7Q5KMFBX4LQ2N', 'name': 'projects/ext-datasets/operations/GH4XP7S2XMN7Q5KMFBX4LQ2N'}\n","Grid 19\n","curr_year 2017\n","Saving data for Pashchim Medinipur 2017\n","Task Started {'state': 'READY', 'description': 'Pashchim Medinipur_19_2017', 'priority': 100, 'creation_timestamp_ms': 1762656316641, 'update_timestamp_ms': 1762656316641, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'IXSCVZEOV5O2BZDCLRYEQEWT', 'name': 'projects/ext-datasets/operations/IXSCVZEOV5O2BZDCLRYEQEWT'}\n","Grid 20\n","curr_year 2017\n","Saving data for Pashchim Medinipur 2017\n","Task Started {'state': 'READY', 'description': 'Pashchim Medinipur_20_2017', 'priority': 100, 'creation_timestamp_ms': 1762656320650, 'update_timestamp_ms': 1762656320650, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'OB6CO3QWKHN5FDR3UYVX55FS', 'name': 'projects/ext-datasets/operations/OB6CO3QWKHN5FDR3UYVX55FS'}\n","Grid 21\n","curr_year 2017\n","Saving data for Pashchim Medinipur 2017\n","Task Started {'state': 'READY', 'description': 'Pashchim Medinipur_21_2017', 'priority': 100, 'creation_timestamp_ms': 1762656326833, 'update_timestamp_ms': 1762656326833, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'CY6HGBILDRRCFQ3IMB52DXRE', 'name': 'projects/ext-datasets/operations/CY6HGBILDRRCFQ3IMB52DXRE'}\n","Grid 22\n","curr_year 2017\n","Saving data for Pashchim Medinipur 2017\n","Task Started {'state': 'READY', 'description': 'Pashchim Medinipur_22_2017', 'priority': 100, 'creation_timestamp_ms': 1762656335955, 'update_timestamp_ms': 1762656335955, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'F7OBIUKKF6JTZXGZQ2AZD4IZ', 'name': 'projects/ext-datasets/operations/F7OBIUKKF6JTZXGZQ2AZD4IZ'}\n","Grid 23\n","curr_year 2017\n","Saving data for Pashchim Medinipur 2017\n","Task Started {'state': 'READY', 'description': 'Pashchim Medinipur_23_2017', 'priority': 100, 'creation_timestamp_ms': 1762656339758, 'update_timestamp_ms': 1762656339758, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'HDOFNZZX3ERQYTN54S2YB5BF', 'name': 'projects/ext-datasets/operations/HDOFNZZX3ERQYTN54S2YB5BF'}\n","Grid 24\n","curr_year 2017\n","Saving data for Pashchim Medinipur 2017\n","Task Started {'state': 'READY', 'description': 'Pashchim Medinipur_24_2017', 'priority': 100, 'creation_timestamp_ms': 1762656346987, 'update_timestamp_ms': 1762656346987, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '2HANCJRX2W33KZQJFG4F56RB', 'name': 'projects/ext-datasets/operations/2HANCJRX2W33KZQJFG4F56RB'}\n","Grid 25\n","curr_year 2017\n","Saving data for Pashchim Medinipur 2017\n","Task Started {'state': 'READY', 'description': 'Pashchim Medinipur_25_2017', 'priority': 100, 'creation_timestamp_ms': 1762656354290, 'update_timestamp_ms': 1762656354290, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'UHONUPREWQUGU3FRAFBP4KZE', 'name': 'projects/ext-datasets/operations/UHONUPREWQUGU3FRAFBP4KZE'}\n","Grid 26\n","curr_year 2017\n","Saving data for Pashchim Medinipur 2017\n","Task Started {'state': 'READY', 'description': 'Pashchim Medinipur_26_2017', 'priority': 100, 'creation_timestamp_ms': 1762656362444, 'update_timestamp_ms': 1762656362444, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'WY4KG7Z4KJWSYPEDMBU4XKBT', 'name': 'projects/ext-datasets/operations/WY4KG7Z4KJWSYPEDMBU4XKBT'}\n","Grid 27\n","curr_year 2017\n","Saving data for Pashchim Medinipur 2017\n","Task Started {'state': 'READY', 'description': 'Pashchim Medinipur_27_2017', 'priority': 100, 'creation_timestamp_ms': 1762656371100, 'update_timestamp_ms': 1762656371100, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '7WIJYTVOPX5ETBM5JGYSFHE2', 'name': 'projects/ext-datasets/operations/7WIJYTVOPX5ETBM5JGYSFHE2'}\n","Grid 28\n","curr_year 2017\n","Saving data for Pashchim Medinipur 2017\n","Task Started {'state': 'READY', 'description': 'Pashchim Medinipur_28_2017', 'priority': 100, 'creation_timestamp_ms': 1762656378402, 'update_timestamp_ms': 1762656378402, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'MFIEIXFUR6Y3UZJEG5MMA5FH', 'name': 'projects/ext-datasets/operations/MFIEIXFUR6Y3UZJEG5MMA5FH'}\n","Grid 29\n","curr_year 2017\n","Saving data for Pashchim Medinipur 2017\n","Task Started {'state': 'READY', 'description': 'Pashchim Medinipur_29_2017', 'priority': 100, 'creation_timestamp_ms': 1762656386206, 'update_timestamp_ms': 1762656386206, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'D7IF32YKD3DAFVPQ5JASEWGS', 'name': 'projects/ext-datasets/operations/D7IF32YKD3DAFVPQ5JASEWGS'}\n","Grid 30\n","curr_year 2017\n","Saving data for Pashchim Medinipur 2017\n","Task Started {'state': 'READY', 'description': 'Pashchim Medinipur_30_2017', 'priority': 100, 'creation_timestamp_ms': 1762656392645, 'update_timestamp_ms': 1762656392645, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'V4WGZ6NBUQQRZ4TXLRTFW2Y3', 'name': 'projects/ext-datasets/operations/V4WGZ6NBUQQRZ4TXLRTFW2Y3'}\n","Grid 31\n","curr_year 2017\n","Saving data for Pashchim Medinipur 2017\n","Task Started {'state': 'READY', 'description': 'Pashchim Medinipur_31_2017', 'priority': 100, 'creation_timestamp_ms': 1762656397093, 'update_timestamp_ms': 1762656397093, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'LXQJV4BS4KANLHELC32HNS2T', 'name': 'projects/ext-datasets/operations/LXQJV4BS4KANLHELC32HNS2T'}\n","Grid 32\n","curr_year 2017\n","Saving data for Pashchim Medinipur 2017\n","Task Started {'state': 'READY', 'description': 'Pashchim Medinipur_32_2017', 'priority': 100, 'creation_timestamp_ms': 1762656404004, 'update_timestamp_ms': 1762656404004, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'K5PVV7G3P7XSJ6BRNGM4QCQH', 'name': 'projects/ext-datasets/operations/K5PVV7G3P7XSJ6BRNGM4QCQH'}\n","Grid 33\n","curr_year 2017\n","Saving data for Pashchim Medinipur 2017\n","Task Started {'state': 'READY', 'description': 'Pashchim Medinipur_33_2017', 'priority': 100, 'creation_timestamp_ms': 1762656411957, 'update_timestamp_ms': 1762656411957, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'MTJ3WWRWD4VLDRNN67JVG6BW', 'name': 'projects/ext-datasets/operations/MTJ3WWRWD4VLDRNN67JVG6BW'}\n","Grid 34\n","curr_year 2017\n","Saving data for Pashchim Medinipur 2017\n","Task Started {'state': 'READY', 'description': 'Pashchim Medinipur_34_2017', 'priority': 100, 'creation_timestamp_ms': 1762656418840, 'update_timestamp_ms': 1762656418840, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'DYO3ALZEAUM2MONCEYUYB24F', 'name': 'projects/ext-datasets/operations/DYO3ALZEAUM2MONCEYUYB24F'}\n","Grid 35\n","curr_year 2017\n","Saving data for Pashchim Medinipur 2017\n","Task Started {'state': 'READY', 'description': 'Pashchim Medinipur_35_2017', 'priority': 100, 'creation_timestamp_ms': 1762656427719, 'update_timestamp_ms': 1762656427719, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'HFOXK4MPZPS3PA5MVDFPINIZ', 'name': 'projects/ext-datasets/operations/HFOXK4MPZPS3PA5MVDFPINIZ'}\n","Grid 36\n","curr_year 2017\n","Saving data for Pashchim Medinipur 2017\n","Task Started {'state': 'READY', 'description': 'Pashchim Medinipur_36_2017', 'priority': 100, 'creation_timestamp_ms': 1762656432553, 'update_timestamp_ms': 1762656432553, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'EATJNAJPJASL6NHED6GBMLC3', 'name': 'projects/ext-datasets/operations/EATJNAJPJASL6NHED6GBMLC3'}\n","Grid 37\n","curr_year 2017\n","Saving data for Pashchim Medinipur 2017\n","Task Started {'state': 'READY', 'description': 'Pashchim Medinipur_37_2017', 'priority': 100, 'creation_timestamp_ms': 1762656442254, 'update_timestamp_ms': 1762656442254, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'OMX24IFOFKEYLPATVQANCTUD', 'name': 'projects/ext-datasets/operations/OMX24IFOFKEYLPATVQANCTUD'}\n","Grid 38\n","curr_year 2017\n","Saving data for Pashchim Medinipur 2017\n","Task Started {'state': 'READY', 'description': 'Pashchim Medinipur_38_2017', 'priority': 100, 'creation_timestamp_ms': 1762656452255, 'update_timestamp_ms': 1762656452255, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'N5RBFHAJFWIIV3V7BNEM46Q2', 'name': 'projects/ext-datasets/operations/N5RBFHAJFWIIV3V7BNEM46Q2'}\n","Grid 39\n","curr_year 2017\n","Saving data for Pashchim Medinipur 2017\n","Task Started {'state': 'READY', 'description': 'Pashchim Medinipur_39_2017', 'priority': 100, 'creation_timestamp_ms': 1762656459895, 'update_timestamp_ms': 1762656459895, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'BJVJXEVOF42MDX5O4LEQU4EU', 'name': 'projects/ext-datasets/operations/BJVJXEVOF42MDX5O4LEQU4EU'}\n","Grid 40\n","curr_year 2017\n","Saving data for Pashchim Medinipur 2017\n","Task Started {'state': 'READY', 'description': 'Pashchim Medinipur_40_2017', 'priority': 100, 'creation_timestamp_ms': 1762656465910, 'update_timestamp_ms': 1762656465910, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'NTCLSHMOPZEWQOBUSF7UZE4Z', 'name': 'projects/ext-datasets/operations/NTCLSHMOPZEWQOBUSF7UZE4Z'}\n","Grid 41\n","curr_year 2017\n","Saving data for Pashchim Medinipur 2017\n","Task Started {'state': 'READY', 'description': 'Pashchim Medinipur_41_2017', 'priority': 100, 'creation_timestamp_ms': 1762656474428, 'update_timestamp_ms': 1762656474428, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'IXNUJJMRO7PZHYZCFWTQS6NU', 'name': 'projects/ext-datasets/operations/IXNUJJMRO7PZHYZCFWTQS6NU'}\n","Grid 42\n","curr_year 2017\n","Saving data for Pashchim Medinipur 2017\n","Task Started {'state': 'READY', 'description': 'Pashchim Medinipur_42_2017', 'priority': 100, 'creation_timestamp_ms': 1762656480606, 'update_timestamp_ms': 1762656480606, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'K5H7C4LNI4Z6IHYTZTSPFUSH', 'name': 'projects/ext-datasets/operations/K5H7C4LNI4Z6IHYTZTSPFUSH'}\n","Grid 43\n","curr_year 2017\n","Saving data for Pashchim Medinipur 2017\n","Task Started {'state': 'READY', 'description': 'Pashchim Medinipur_43_2017', 'priority': 100, 'creation_timestamp_ms': 1762656487456, 'update_timestamp_ms': 1762656487456, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'S5DFHBRCBKCD3MAV2AIJJ5LN', 'name': 'projects/ext-datasets/operations/S5DFHBRCBKCD3MAV2AIJJ5LN'}\n","Grid 44\n","curr_year 2017\n","Saving data for Pashchim Medinipur 2017\n","Task Started {'state': 'READY', 'description': 'Pashchim Medinipur_44_2017', 'priority': 100, 'creation_timestamp_ms': 1762656495657, 'update_timestamp_ms': 1762656495657, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'AZOOVCQRKGQGW4GYQ6OSGTDI', 'name': 'projects/ext-datasets/operations/AZOOVCQRKGQGW4GYQ6OSGTDI'}\n","Grid 45\n","curr_year 2017\n","Saving data for Pashchim Medinipur 2017\n","Task Started {'state': 'READY', 'description': 'Pashchim Medinipur_45_2017', 'priority': 100, 'creation_timestamp_ms': 1762656505780, 'update_timestamp_ms': 1762656505780, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'K3KNLJXFSBGO6SCJDGHGW3BE', 'name': 'projects/ext-datasets/operations/K3KNLJXFSBGO6SCJDGHGW3BE'}\n","Grid 46\n","curr_year 2017\n","Saving data for Pashchim Medinipur 2017\n","Task Started {'state': 'READY', 'description': 'Pashchim Medinipur_46_2017', 'priority': 100, 'creation_timestamp_ms': 1762656512324, 'update_timestamp_ms': 1762656512324, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'BFE4HB7KGSK6ZS6RBG337BXT', 'name': 'projects/ext-datasets/operations/BFE4HB7KGSK6ZS6RBG337BXT'}\n","Grid 47\n","curr_year 2017\n","Saving data for Pashchim Medinipur 2017\n","Task Started {'state': 'READY', 'description': 'Pashchim Medinipur_47_2017', 'priority': 100, 'creation_timestamp_ms': 1762656518176, 'update_timestamp_ms': 1762656518176, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'TRNVLEFFW3B4GSDB5UWPPUB4', 'name': 'projects/ext-datasets/operations/TRNVLEFFW3B4GSDB5UWPPUB4'}\n","Grid 48\n","curr_year 2017\n","Saving data for Pashchim Medinipur 2017\n","Task Started {'state': 'READY', 'description': 'Pashchim Medinipur_48_2017', 'priority': 100, 'creation_timestamp_ms': 1762656527454, 'update_timestamp_ms': 1762656527454, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'JGRJKCYFQZY3GLDBVXF4VL5W', 'name': 'projects/ext-datasets/operations/JGRJKCYFQZY3GLDBVXF4VL5W'}\n","Grid 49\n","curr_year 2017\n","Saving data for Pashchim Medinipur 2017\n","Task Started {'state': 'READY', 'description': 'Pashchim Medinipur_49_2017', 'priority': 100, 'creation_timestamp_ms': 1762656534302, 'update_timestamp_ms': 1762656534302, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'TVFKBPWGU3I2ANR5ZZ62QQ7V', 'name': 'projects/ext-datasets/operations/TVFKBPWGU3I2ANR5ZZ62QQ7V'}\n","Grid 50\n","curr_year 2017\n","Saving data for Pashchim Medinipur 2017\n","Task Started {'state': 'READY', 'description': 'Pashchim Medinipur_50_2017', 'priority': 100, 'creation_timestamp_ms': 1762656538293, 'update_timestamp_ms': 1762656538293, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'QH4Y4UB2AEIVTKHSDZSZP77E', 'name': 'projects/ext-datasets/operations/QH4Y4UB2AEIVTKHSDZSZP77E'}\n","Grid 51\n","curr_year 2017\n","Saving data for Pashchim Medinipur 2017\n","Task Started {'state': 'READY', 'description': 'Pashchim Medinipur_51_2017', 'priority': 100, 'creation_timestamp_ms': 1762656545479, 'update_timestamp_ms': 1762656545479, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '3GBB4YCPVFLWB3R5ISX5RBOA', 'name': 'projects/ext-datasets/operations/3GBB4YCPVFLWB3R5ISX5RBOA'}\n","Grid 52\n","curr_year 2017\n","Saving data for Pashchim Medinipur 2017\n","Task Started {'state': 'READY', 'description': 'Pashchim Medinipur_52_2017', 'priority': 100, 'creation_timestamp_ms': 1762656552841, 'update_timestamp_ms': 1762656552841, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'NAMJHCTMIPZ7EVBAMWVP2UXC', 'name': 'projects/ext-datasets/operations/NAMJHCTMIPZ7EVBAMWVP2UXC'}\n","Grid 53\n","curr_year 2017\n","Saving data for Pashchim Medinipur 2017\n","Task Started {'state': 'READY', 'description': 'Pashchim Medinipur_53_2017', 'priority': 100, 'creation_timestamp_ms': 1762656560184, 'update_timestamp_ms': 1762656560184, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'YLAZNLYRQ7CN3HQFGUAQYDR7', 'name': 'projects/ext-datasets/operations/YLAZNLYRQ7CN3HQFGUAQYDR7'}\n","Grid 54\n","curr_year 2017\n","Saving data for Pashchim Medinipur 2017\n","Task Started {'state': 'READY', 'description': 'Pashchim Medinipur_54_2017', 'priority': 100, 'creation_timestamp_ms': 1762656566137, 'update_timestamp_ms': 1762656566137, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'SENC3EBMKCJ6DBXFPTFHJT73', 'name': 'projects/ext-datasets/operations/SENC3EBMKCJ6DBXFPTFHJT73'}\n","Grid 55\n","curr_year 2017\n","Saving data for Pashchim Medinipur 2017\n","Task Started {'state': 'READY', 'description': 'Pashchim Medinipur_55_2017', 'priority': 100, 'creation_timestamp_ms': 1762656573607, 'update_timestamp_ms': 1762656573607, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'P2BS6PMTLVP54KI4SR3444TD', 'name': 'projects/ext-datasets/operations/P2BS6PMTLVP54KI4SR3444TD'}\n","Grid 56\n","curr_year 2017\n","Saving data for Pashchim Medinipur 2017\n","Task Started {'state': 'READY', 'description': 'Pashchim Medinipur_56_2017', 'priority': 100, 'creation_timestamp_ms': 1762656579996, 'update_timestamp_ms': 1762656579996, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'BNQELTIGCTOOI2DOJ53ZQVHE', 'name': 'projects/ext-datasets/operations/BNQELTIGCTOOI2DOJ53ZQVHE'}\n","Grid 57\n","curr_year 2017\n","Saving data for Pashchim Medinipur 2017\n","Task Started {'state': 'READY', 'description': 'Pashchim Medinipur_57_2017', 'priority': 100, 'creation_timestamp_ms': 1762656588102, 'update_timestamp_ms': 1762656588102, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'K2IKQ44ZFZP3FP6OKVJ7UKPY', 'name': 'projects/ext-datasets/operations/K2IKQ44ZFZP3FP6OKVJ7UKPY'}\n","Grid 58\n","curr_year 2017\n","Saving data for Pashchim Medinipur 2017\n","Task Started {'state': 'READY', 'description': 'Pashchim Medinipur_58_2017', 'priority': 100, 'creation_timestamp_ms': 1762656593522, 'update_timestamp_ms': 1762656593522, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'DV6XT6WGMSCKULSFVXXOBTNP', 'name': 'projects/ext-datasets/operations/DV6XT6WGMSCKULSFVXXOBTNP'}\n","Grid 59\n","curr_year 2017\n","Saving data for Pashchim Medinipur 2017\n","Task Started {'state': 'READY', 'description': 'Pashchim Medinipur_59_2017', 'priority': 100, 'creation_timestamp_ms': 1762656596924, 'update_timestamp_ms': 1762656596924, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'ZOMXOXZH2QWG5CEYSPLAAA42', 'name': 'projects/ext-datasets/operations/ZOMXOXZH2QWG5CEYSPLAAA42'}\n","Grid 60\n","curr_year 2017\n","Saving data for Pashchim Medinipur 2017\n","Task Started {'state': 'READY', 'description': 'Pashchim Medinipur_60_2017', 'priority': 100, 'creation_timestamp_ms': 1762656600989, 'update_timestamp_ms': 1762656600989, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '53Y6PSKHDAKNNLWUAEBJUFYR', 'name': 'projects/ext-datasets/operations/53Y6PSKHDAKNNLWUAEBJUFYR'}\n","Grid 61\n","curr_year 2017\n","Saving data for Pashchim Medinipur 2017\n","Task Started {'state': 'READY', 'description': 'Pashchim Medinipur_61_2017', 'priority': 100, 'creation_timestamp_ms': 1762656613380, 'update_timestamp_ms': 1762656613380, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'SN3YIREDMLZITHQQWJERXFR7', 'name': 'projects/ext-datasets/operations/SN3YIREDMLZITHQQWJERXFR7'}\n","Grid 62\n","curr_year 2017\n","Saving data for Pashchim Medinipur 2017\n","Task Started {'state': 'READY', 'description': 'Pashchim Medinipur_62_2017', 'priority': 100, 'creation_timestamp_ms': 1762656620943, 'update_timestamp_ms': 1762656620943, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'KNCWZK6F2XN6MKPJO6HVYXVE', 'name': 'projects/ext-datasets/operations/KNCWZK6F2XN6MKPJO6HVYXVE'}\n","Grid 63\n","curr_year 2017\n","Saving data for Pashchim Medinipur 2017\n","Task Started {'state': 'READY', 'description': 'Pashchim Medinipur_63_2017', 'priority': 100, 'creation_timestamp_ms': 1762656625158, 'update_timestamp_ms': 1762656625158, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'VAU3N6VKUG5HETJN7AW3SK2I', 'name': 'projects/ext-datasets/operations/VAU3N6VKUG5HETJN7AW3SK2I'}\n","Grid 64\n","curr_year 2017\n","Saving data for Pashchim Medinipur 2017\n","Task Started {'state': 'READY', 'description': 'Pashchim Medinipur_64_2017', 'priority': 100, 'creation_timestamp_ms': 1762656631973, 'update_timestamp_ms': 1762656631973, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'L754LPKGNNRQ6N5ESAPOXWZM', 'name': 'projects/ext-datasets/operations/L754LPKGNNRQ6N5ESAPOXWZM'}\n","Grid 65\n","curr_year 2017\n","Saving data for Pashchim Medinipur 2017\n","Task Started {'state': 'READY', 'description': 'Pashchim Medinipur_65_2017', 'priority': 100, 'creation_timestamp_ms': 1762656639958, 'update_timestamp_ms': 1762656639958, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '6RWXDNP6BLABZPKXMZK5YC2V', 'name': 'projects/ext-datasets/operations/6RWXDNP6BLABZPKXMZK5YC2V'}\n","Grid 66\n","curr_year 2017\n","Saving data for Pashchim Medinipur 2017\n","Task Started {'state': 'READY', 'description': 'Pashchim Medinipur_66_2017', 'priority': 100, 'creation_timestamp_ms': 1762656646795, 'update_timestamp_ms': 1762656646795, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'TR3RQIKCRZZDCTFL6H35VVEL', 'name': 'projects/ext-datasets/operations/TR3RQIKCRZZDCTFL6H35VVEL'}\n","Grid 67\n","curr_year 2017\n","Saving data for Pashchim Medinipur 2017\n","Task Started {'state': 'READY', 'description': 'Pashchim Medinipur_67_2017', 'priority': 100, 'creation_timestamp_ms': 1762656654192, 'update_timestamp_ms': 1762656654192, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '3536G55NHWZJRXCUNX5INMMX', 'name': 'projects/ext-datasets/operations/3536G55NHWZJRXCUNX5INMMX'}\n","Grid 68\n","curr_year 2017\n","Saving data for Pashchim Medinipur 2017\n","Task Started {'state': 'READY', 'description': 'Pashchim Medinipur_68_2017', 'priority': 100, 'creation_timestamp_ms': 1762656657940, 'update_timestamp_ms': 1762656657940, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '5AP4XWHGCVSSYF276DXPUXKY', 'name': 'projects/ext-datasets/operations/5AP4XWHGCVSSYF276DXPUXKY'}\n","Grid 69\n","curr_year 2017\n","Saving data for Pashchim Medinipur 2017\n","Task Started {'state': 'READY', 'description': 'Pashchim Medinipur_69_2017', 'priority': 100, 'creation_timestamp_ms': 1762656665004, 'update_timestamp_ms': 1762656665004, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '5CBUYVJFTEXYX2LON3G6ZWWT', 'name': 'projects/ext-datasets/operations/5CBUYVJFTEXYX2LON3G6ZWWT'}\n","Grid 70\n","curr_year 2017\n","Saving data for Pashchim Medinipur 2017\n","Task Started {'state': 'READY', 'description': 'Pashchim Medinipur_70_2017', 'priority': 100, 'creation_timestamp_ms': 1762656672582, 'update_timestamp_ms': 1762656672582, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '47V4ESB3D6M22D52LU4XKILJ', 'name': 'projects/ext-datasets/operations/47V4ESB3D6M22D52LU4XKILJ'}\n","Grid 71\n","curr_year 2017\n","Saving data for Pashchim Medinipur 2017\n","Task Started {'state': 'READY', 'description': 'Pashchim Medinipur_71_2017', 'priority': 100, 'creation_timestamp_ms': 1762656676519, 'update_timestamp_ms': 1762656676519, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'GPINVNQTY3O6B5Q3ZW3B6ICL', 'name': 'projects/ext-datasets/operations/GPINVNQTY3O6B5Q3ZW3B6ICL'}\n","Grid 72\n","curr_year 2017\n","Saving data for Pashchim Medinipur 2017\n","Task Started {'state': 'READY', 'description': 'Pashchim Medinipur_72_2017', 'priority': 100, 'creation_timestamp_ms': 1762656683911, 'update_timestamp_ms': 1762656683911, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'BAAKIKPXZO55ODDFIRL54NBO', 'name': 'projects/ext-datasets/operations/BAAKIKPXZO55ODDFIRL54NBO'}\n","Grid 73\n","curr_year 2017\n","Saving data for Pashchim Medinipur 2017\n","Task Started {'state': 'READY', 'description': 'Pashchim Medinipur_73_2017', 'priority': 100, 'creation_timestamp_ms': 1762656690478, 'update_timestamp_ms': 1762656690478, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'J32725Z3XMKNYRHR2DFJEIED', 'name': 'projects/ext-datasets/operations/J32725Z3XMKNYRHR2DFJEIED'}\n","Grid 74\n","curr_year 2017\n","Saving data for Pashchim Medinipur 2017\n","Task Started {'state': 'READY', 'description': 'Pashchim Medinipur_74_2017', 'priority': 100, 'creation_timestamp_ms': 1762656696440, 'update_timestamp_ms': 1762656696440, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'ICSQFWP3TWZH757K3KWJV4AM', 'name': 'projects/ext-datasets/operations/ICSQFWP3TWZH757K3KWJV4AM'}\n","Grid 75\n","curr_year 2017\n","Saving data for Pashchim Medinipur 2017\n","Task Started {'state': 'READY', 'description': 'Pashchim Medinipur_75_2017', 'priority': 100, 'creation_timestamp_ms': 1762656705583, 'update_timestamp_ms': 1762656705583, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'BUDJCDIJ5BXY4I7X6V2VXOVS', 'name': 'projects/ext-datasets/operations/BUDJCDIJ5BXY4I7X6V2VXOVS'}\n","Grid 76\n","curr_year 2017\n","Saving data for Pashchim Medinipur 2017\n","Task Started {'state': 'READY', 'description': 'Pashchim Medinipur_76_2017', 'priority': 100, 'creation_timestamp_ms': 1762656714025, 'update_timestamp_ms': 1762656714025, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'XM7PPW7FECNNNKQ6M3O66TK2', 'name': 'projects/ext-datasets/operations/XM7PPW7FECNNNKQ6M3O66TK2'}\n","Grid 77\n","curr_year 2017\n","Saving data for Pashchim Medinipur 2017\n","Task Started {'state': 'READY', 'description': 'Pashchim Medinipur_77_2017', 'priority': 100, 'creation_timestamp_ms': 1762656721691, 'update_timestamp_ms': 1762656721691, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '2VKXQCYRMV5WIVN6ELMCSWLA', 'name': 'projects/ext-datasets/operations/2VKXQCYRMV5WIVN6ELMCSWLA'}\n","Grid 78\n","curr_year 2017\n","Saving data for Pashchim Medinipur 2017\n","Task Started {'state': 'READY', 'description': 'Pashchim Medinipur_78_2017', 'priority': 100, 'creation_timestamp_ms': 1762656729644, 'update_timestamp_ms': 1762656729644, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'IKCUB72MJN4NYMBZ7CZ6KRLJ', 'name': 'projects/ext-datasets/operations/IKCUB72MJN4NYMBZ7CZ6KRLJ'}\n","Grid 79\n","curr_year 2017\n","Saving data for Pashchim Medinipur 2017\n","Task Started {'state': 'READY', 'description': 'Pashchim Medinipur_79_2017', 'priority': 100, 'creation_timestamp_ms': 1762656736150, 'update_timestamp_ms': 1762656736150, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '3NS6QI5NYBN5WLJGMLLMQYCZ', 'name': 'projects/ext-datasets/operations/3NS6QI5NYBN5WLJGMLLMQYCZ'}\n","Grid 80\n","curr_year 2017\n","Saving data for Pashchim Medinipur 2017\n","Task Started {'state': 'READY', 'description': 'Pashchim Medinipur_80_2017', 'priority': 100, 'creation_timestamp_ms': 1762656744215, 'update_timestamp_ms': 1762656744215, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'C5DMPQANFPK6R6JRXP4OZOIU', 'name': 'projects/ext-datasets/operations/C5DMPQANFPK6R6JRXP4OZOIU'}\n","Grid 81\n","curr_year 2017\n","Saving data for Pashchim Medinipur 2017\n","Task Started {'state': 'READY', 'description': 'Pashchim Medinipur_81_2017', 'priority': 100, 'creation_timestamp_ms': 1762656751264, 'update_timestamp_ms': 1762656751264, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'MUK6GYTC6RFW5MGWS5ZFPXD6', 'name': 'projects/ext-datasets/operations/MUK6GYTC6RFW5MGWS5ZFPXD6'}\n","Grid 82\n","curr_year 2017\n","Saving data for Pashchim Medinipur 2017\n","Task Started {'state': 'READY', 'description': 'Pashchim Medinipur_82_2017', 'priority': 100, 'creation_timestamp_ms': 1762656757892, 'update_timestamp_ms': 1762656757892, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'C5AYSNYGXU25AAVQO4W6PCCH', 'name': 'projects/ext-datasets/operations/C5AYSNYGXU25AAVQO4W6PCCH'}\n","Grid 83\n","curr_year 2017\n","Saving data for Pashchim Medinipur 2017\n","Task Started {'state': 'READY', 'description': 'Pashchim Medinipur_83_2017', 'priority': 100, 'creation_timestamp_ms': 1762656766108, 'update_timestamp_ms': 1762656766108, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'JZ5XQCLNBBW2UWEYUAMHFA7W', 'name': 'projects/ext-datasets/operations/JZ5XQCLNBBW2UWEYUAMHFA7W'}\n","Grid 84\n","curr_year 2017\n","Saving data for Pashchim Medinipur 2017\n","Task Started {'state': 'READY', 'description': 'Pashchim Medinipur_84_2017', 'priority': 100, 'creation_timestamp_ms': 1762656773523, 'update_timestamp_ms': 1762656773523, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'GTSDY74FH5Q7LZ6PUWZMTEQF', 'name': 'projects/ext-datasets/operations/GTSDY74FH5Q7LZ6PUWZMTEQF'}\n","Grid 85\n","curr_year 2017\n","Saving data for Pashchim Medinipur 2017\n","Task Started {'state': 'READY', 'description': 'Pashchim Medinipur_85_2017', 'priority': 100, 'creation_timestamp_ms': 1762656780140, 'update_timestamp_ms': 1762656780140, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '4UDWEDAOVC7WA3PDZSQD5VKI', 'name': 'projects/ext-datasets/operations/4UDWEDAOVC7WA3PDZSQD5VKI'}\n","Grid 86\n","curr_year 2017\n","Saving data for Pashchim Medinipur 2017\n","Task Started {'state': 'READY', 'description': 'Pashchim Medinipur_86_2017', 'priority': 100, 'creation_timestamp_ms': 1762656787998, 'update_timestamp_ms': 1762656787998, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'ZB62CJ4FM2LRBQGNPFCHWOQN', 'name': 'projects/ext-datasets/operations/ZB62CJ4FM2LRBQGNPFCHWOQN'}\n","Grid 87\n","curr_year 2017\n","Saving data for Pashchim Medinipur 2017\n","Task Started {'state': 'READY', 'description': 'Pashchim Medinipur_87_2017', 'priority': 100, 'creation_timestamp_ms': 1762656796603, 'update_timestamp_ms': 1762656796603, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'UJZBUDA55TS4UTZ5QNACJROS', 'name': 'projects/ext-datasets/operations/UJZBUDA55TS4UTZ5QNACJROS'}\n","Grid 88\n","curr_year 2017\n","Saving data for Pashchim Medinipur 2017\n","Task Started {'state': 'READY', 'description': 'Pashchim Medinipur_88_2017', 'priority': 100, 'creation_timestamp_ms': 1762656803339, 'update_timestamp_ms': 1762656803339, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'PPYIAGJK2JSNFJ2ZSUY5YUUI', 'name': 'projects/ext-datasets/operations/PPYIAGJK2JSNFJ2ZSUY5YUUI'}\n","Grid 89\n","curr_year 2017\n","Saving data for Pashchim Medinipur 2017\n","Task Started {'state': 'READY', 'description': 'Pashchim Medinipur_89_2017', 'priority': 100, 'creation_timestamp_ms': 1762656811006, 'update_timestamp_ms': 1762656811006, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'DSMVS6TSJ5XCD4P3WSFZXBXN', 'name': 'projects/ext-datasets/operations/DSMVS6TSJ5XCD4P3WSFZXBXN'}\n","Grid 90\n","curr_year 2017\n","Saving data for Pashchim Medinipur 2017\n","Task Started {'state': 'READY', 'description': 'Pashchim Medinipur_90_2017', 'priority': 100, 'creation_timestamp_ms': 1762656818857, 'update_timestamp_ms': 1762656818857, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'LW3GQQMPYOUJND54C57CMQET', 'name': 'projects/ext-datasets/operations/LW3GQQMPYOUJND54C57CMQET'}\n","Grid 91\n","curr_year 2017\n","Saving data for Pashchim Medinipur 2017\n","Task Started {'state': 'READY', 'description': 'Pashchim Medinipur_91_2017', 'priority': 100, 'creation_timestamp_ms': 1762656825725, 'update_timestamp_ms': 1762656825725, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'XYBPIQYELLUCX73RQYL4DPT4', 'name': 'projects/ext-datasets/operations/XYBPIQYELLUCX73RQYL4DPT4'}\n","Grid 92\n","curr_year 2017\n","Saving data for Pashchim Medinipur 2017\n","Task Started {'state': 'READY', 'description': 'Pashchim Medinipur_92_2017', 'priority': 100, 'creation_timestamp_ms': 1762656829437, 'update_timestamp_ms': 1762656829437, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'N7QT4WOOFY3NT2RW2XDD54LV', 'name': 'projects/ext-datasets/operations/N7QT4WOOFY3NT2RW2XDD54LV'}\n","Grid 93\n","curr_year 2017\n","Saving data for Pashchim Medinipur 2017\n","Task Started {'state': 'READY', 'description': 'Pashchim Medinipur_93_2017', 'priority': 100, 'creation_timestamp_ms': 1762656836388, 'update_timestamp_ms': 1762656836388, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'U3FNIEREMAZJM3MVYKRIRQCW', 'name': 'projects/ext-datasets/operations/U3FNIEREMAZJM3MVYKRIRQCW'}\n","Grid 94\n","curr_year 2017\n","Saving data for Pashchim Medinipur 2017\n","Task Started {'state': 'READY', 'description': 'Pashchim Medinipur_94_2017', 'priority': 100, 'creation_timestamp_ms': 1762656844471, 'update_timestamp_ms': 1762656844471, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '7KEVA4YXW4LZDTKJMGDEBA23', 'name': 'projects/ext-datasets/operations/7KEVA4YXW4LZDTKJMGDEBA23'}\n","Grid 95\n","curr_year 2017\n","Saving data for Pashchim Medinipur 2017\n","Task Started {'state': 'READY', 'description': 'Pashchim Medinipur_95_2017', 'priority': 100, 'creation_timestamp_ms': 1762656851187, 'update_timestamp_ms': 1762656851187, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'TYJGQKQOLJNDSAZRI37YNKZS', 'name': 'projects/ext-datasets/operations/TYJGQKQOLJNDSAZRI37YNKZS'}\n","Grid 96\n","curr_year 2017\n","Saving data for Pashchim Medinipur 2017\n","Task Started {'state': 'READY', 'description': 'Pashchim Medinipur_96_2017', 'priority': 100, 'creation_timestamp_ms': 1762656858006, 'update_timestamp_ms': 1762656858006, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'M7NG73K2EKRNO7LQMGFV3AOH', 'name': 'projects/ext-datasets/operations/M7NG73K2EKRNO7LQMGFV3AOH'}\n","Grid 97\n","curr_year 2017\n","Saving data for Pashchim Medinipur 2017\n","Task Started {'state': 'READY', 'description': 'Pashchim Medinipur_97_2017', 'priority': 100, 'creation_timestamp_ms': 1762656865190, 'update_timestamp_ms': 1762656865190, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '7ASTVLZ4NF3MPHDM567SFJVT', 'name': 'projects/ext-datasets/operations/7ASTVLZ4NF3MPHDM567SFJVT'}\n","Grid 98\n","curr_year 2017\n","Saving data for Pashchim Medinipur 2017\n","Task Started {'state': 'READY', 'description': 'Pashchim Medinipur_98_2017', 'priority': 100, 'creation_timestamp_ms': 1762656869442, 'update_timestamp_ms': 1762656869442, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '4XMKVY3QE4XT752M4CLLUTIG', 'name': 'projects/ext-datasets/operations/4XMKVY3QE4XT752M4CLLUTIG'}\n","Grid 99\n","curr_year 2017\n","Saving data for Pashchim Medinipur 2017\n","Task Started {'state': 'READY', 'description': 'Pashchim Medinipur_99_2017', 'priority': 100, 'creation_timestamp_ms': 1762656876779, 'update_timestamp_ms': 1762656876779, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'ZWD2ZDDWWX45EAEPEB65J3K7', 'name': 'projects/ext-datasets/operations/ZWD2ZDDWWX45EAEPEB65J3K7'}\n","Grid 100\n","curr_year 2017\n","Saving data for Pashchim Medinipur 2017\n","Task Started {'state': 'READY', 'description': 'Pashchim Medinipur_100_2017', 'priority': 100, 'creation_timestamp_ms': 1762656883926, 'update_timestamp_ms': 1762656883926, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'GT753YC5YE7L3J3LYFNTRGD4', 'name': 'projects/ext-datasets/operations/GT753YC5YE7L3J3LYFNTRGD4'}\n","Grid 101\n","curr_year 2017\n","Saving data for Pashchim Medinipur 2017\n","Task Started {'state': 'READY', 'description': 'Pashchim Medinipur_101_2017', 'priority': 100, 'creation_timestamp_ms': 1762656887934, 'update_timestamp_ms': 1762656887934, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'L3YFCSA2TL6YTS3GLFOADGST', 'name': 'projects/ext-datasets/operations/L3YFCSA2TL6YTS3GLFOADGST'}\n","Grid 102\n","curr_year 2017\n","Saving data for Pashchim Medinipur 2017\n","Task Started {'state': 'READY', 'description': 'Pashchim Medinipur_102_2017', 'priority': 100, 'creation_timestamp_ms': 1762656892037, 'update_timestamp_ms': 1762656892037, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'RLR6X7Y7VREGJ7ZY2PDSEZQW', 'name': 'projects/ext-datasets/operations/RLR6X7Y7VREGJ7ZY2PDSEZQW'}\n","Grid 103\n","curr_year 2017\n","Saving data for Pashchim Medinipur 2017\n","Task Started {'state': 'READY', 'description': 'Pashchim Medinipur_103_2017', 'priority': 100, 'creation_timestamp_ms': 1762656900537, 'update_timestamp_ms': 1762656900537, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'C6GJS2NQGLNEWJANSA7AH3EE', 'name': 'projects/ext-datasets/operations/C6GJS2NQGLNEWJANSA7AH3EE'}\n","Grid 104\n","curr_year 2017\n","Saving data for Pashchim Medinipur 2017\n","Task Started {'state': 'READY', 'description': 'Pashchim Medinipur_104_2017', 'priority': 100, 'creation_timestamp_ms': 1762656904748, 'update_timestamp_ms': 1762656904748, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '4MZT6NOPFNCEDHHGRFNUL453', 'name': 'projects/ext-datasets/operations/4MZT6NOPFNCEDHHGRFNUL453'}\n","Grid 105\n","curr_year 2017\n","Saving data for Pashchim Medinipur 2017\n","Task Started {'state': 'READY', 'description': 'Pashchim Medinipur_105_2017', 'priority': 100, 'creation_timestamp_ms': 1762656913234, 'update_timestamp_ms': 1762656913234, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '7W6MQEGDDUFYQGTB6A5GXM4W', 'name': 'projects/ext-datasets/operations/7W6MQEGDDUFYQGTB6A5GXM4W'}\n","Grid 106\n","curr_year 2017\n","Saving data for Pashchim Medinipur 2017\n","Task Started {'state': 'READY', 'description': 'Pashchim Medinipur_106_2017', 'priority': 100, 'creation_timestamp_ms': 1762656922137, 'update_timestamp_ms': 1762656922137, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'XBGPKGS4G6PYW7DK2AZMHSRM', 'name': 'projects/ext-datasets/operations/XBGPKGS4G6PYW7DK2AZMHSRM'}\n","Grid 107\n","curr_year 2017\n","Saving data for Pashchim Medinipur 2017\n","Task Started {'state': 'READY', 'description': 'Pashchim Medinipur_107_2017', 'priority': 100, 'creation_timestamp_ms': 1762656928297, 'update_timestamp_ms': 1762656928297, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'XSMYFCI23ZGDWCISG7PNNHSI', 'name': 'projects/ext-datasets/operations/XSMYFCI23ZGDWCISG7PNNHSI'}\n","Grid 108\n","curr_year 2017\n","Saving data for Pashchim Medinipur 2017\n","Task Started {'state': 'READY', 'description': 'Pashchim Medinipur_108_2017', 'priority': 100, 'creation_timestamp_ms': 1762656934892, 'update_timestamp_ms': 1762656934892, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'METXYW4NNU7GOR5QNQ55N2IA', 'name': 'projects/ext-datasets/operations/METXYW4NNU7GOR5QNQ55N2IA'}\n","Grid 109\n","curr_year 2017\n","Saving data for Pashchim Medinipur 2017\n","Task Started {'state': 'READY', 'description': 'Pashchim Medinipur_109_2017', 'priority': 100, 'creation_timestamp_ms': 1762656942637, 'update_timestamp_ms': 1762656942637, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'BVCD7DLCYT6UTC67HZSHSQS3', 'name': 'projects/ext-datasets/operations/BVCD7DLCYT6UTC67HZSHSQS3'}\n","Grid 110\n","curr_year 2017\n","Saving data for Pashchim Medinipur 2017\n","Task Started {'state': 'READY', 'description': 'Pashchim Medinipur_110_2017', 'priority': 100, 'creation_timestamp_ms': 1762656951277, 'update_timestamp_ms': 1762656951277, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'E2WUB6OT4IXJTAP3PKKESEPI', 'name': 'projects/ext-datasets/operations/E2WUB6OT4IXJTAP3PKKESEPI'}\n","Grid 111\n","curr_year 2017\n","Saving data for Pashchim Medinipur 2017\n","Task Started {'state': 'READY', 'description': 'Pashchim Medinipur_111_2017', 'priority': 100, 'creation_timestamp_ms': 1762656959080, 'update_timestamp_ms': 1762656959080, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '2RZVNFL6BGNK6Z5CCYKE5KIZ', 'name': 'projects/ext-datasets/operations/2RZVNFL6BGNK6Z5CCYKE5KIZ'}\n","Grid 112\n","curr_year 2017\n","Saving data for Pashchim Medinipur 2017\n","Task Started {'state': 'READY', 'description': 'Pashchim Medinipur_112_2017', 'priority': 100, 'creation_timestamp_ms': 1762656962562, 'update_timestamp_ms': 1762656962562, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'XCZSBBI3H3732SCYAI6UZ7LR', 'name': 'projects/ext-datasets/operations/XCZSBBI3H3732SCYAI6UZ7LR'}\n","Grid 113\n","curr_year 2017\n","Saving data for Pashchim Medinipur 2017\n","Task Started {'state': 'READY', 'description': 'Pashchim Medinipur_113_2017', 'priority': 100, 'creation_timestamp_ms': 1762656966527, 'update_timestamp_ms': 1762656966527, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'BUGV2EWOG2CHKPF5F453MEE7', 'name': 'projects/ext-datasets/operations/BUGV2EWOG2CHKPF5F453MEE7'}\n","Grid 114\n","curr_year 2017\n","Saving data for Pashchim Medinipur 2017\n","Task Started {'state': 'READY', 'description': 'Pashchim Medinipur_114_2017', 'priority': 100, 'creation_timestamp_ms': 1762656974449, 'update_timestamp_ms': 1762656974449, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'Y5QPJKP2AUXCVSHP7P7PN2ZO', 'name': 'projects/ext-datasets/operations/Y5QPJKP2AUXCVSHP7P7PN2ZO'}\n","Grid 115\n","curr_year 2017\n","Saving data for Pashchim Medinipur 2017\n","Task Started {'state': 'READY', 'description': 'Pashchim Medinipur_115_2017', 'priority': 100, 'creation_timestamp_ms': 1762656983994, 'update_timestamp_ms': 1762656983994, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '2F3ORFW6VJNRZ3DFENH53KJU', 'name': 'projects/ext-datasets/operations/2F3ORFW6VJNRZ3DFENH53KJU'}\n","Grid 116\n","curr_year 2017\n","Saving data for Pashchim Medinipur 2017\n","Task Started {'state': 'READY', 'description': 'Pashchim Medinipur_116_2017', 'priority': 100, 'creation_timestamp_ms': 1762656988094, 'update_timestamp_ms': 1762656988094, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'JXTE3DZQAQTAVLOWX4DT6UBR', 'name': 'projects/ext-datasets/operations/JXTE3DZQAQTAVLOWX4DT6UBR'}\n","Grid 117\n","curr_year 2017\n","Saving data for Pashchim Medinipur 2017\n","Task Started {'state': 'READY', 'description': 'Pashchim Medinipur_117_2017', 'priority': 100, 'creation_timestamp_ms': 1762656996237, 'update_timestamp_ms': 1762656996237, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'C7YIAUKEHE7T5ZWI677IWWAC', 'name': 'projects/ext-datasets/operations/C7YIAUKEHE7T5ZWI677IWWAC'}\n","Grid 118\n","curr_year 2017\n","Saving data for Pashchim Medinipur 2017\n","Task Started {'state': 'READY', 'description': 'Pashchim Medinipur_118_2017', 'priority': 100, 'creation_timestamp_ms': 1762657000170, 'update_timestamp_ms': 1762657000170, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'WMVWOGJPZL4JGZQEDDH7OZWU', 'name': 'projects/ext-datasets/operations/WMVWOGJPZL4JGZQEDDH7OZWU'}\n","Grid 119\n","curr_year 2017\n","Saving data for Pashchim Medinipur 2017\n","Task Started {'state': 'READY', 'description': 'Pashchim Medinipur_119_2017', 'priority': 100, 'creation_timestamp_ms': 1762657004443, 'update_timestamp_ms': 1762657004443, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '6JX7VU44KIYFEV3QFUQ7AVMC', 'name': 'projects/ext-datasets/operations/6JX7VU44KIYFEV3QFUQ7AVMC'}\n","Grid 120\n","curr_year 2017\n","Saving data for Pashchim Medinipur 2017\n","Task Started {'state': 'READY', 'description': 'Pashchim Medinipur_120_2017', 'priority': 100, 'creation_timestamp_ms': 1762657011353, 'update_timestamp_ms': 1762657011353, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'J3YRSQKUOEUKAIZWRAJIVGNO', 'name': 'projects/ext-datasets/operations/J3YRSQKUOEUKAIZWRAJIVGNO'}\n","Grid 121\n","curr_year 2017\n","Saving data for Pashchim Medinipur 2017\n","Task Started {'state': 'READY', 'description': 'Pashchim Medinipur_121_2017', 'priority': 100, 'creation_timestamp_ms': 1762657020222, 'update_timestamp_ms': 1762657020222, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'OTGVIPC6DG2X2IFLRARVM37O', 'name': 'projects/ext-datasets/operations/OTGVIPC6DG2X2IFLRARVM37O'}\n","Grid 122\n","curr_year 2017\n","Saving data for Pashchim Medinipur 2017\n","Task Started {'state': 'READY', 'description': 'Pashchim Medinipur_122_2017', 'priority': 100, 'creation_timestamp_ms': 1762657027173, 'update_timestamp_ms': 1762657027173, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'H6C6L7WSL4XAO7IZHUYSNQQH', 'name': 'projects/ext-datasets/operations/H6C6L7WSL4XAO7IZHUYSNQQH'}\n","Grid 123\n","curr_year 2017\n","Saving data for Pashchim Medinipur 2017\n","Task Started {'state': 'READY', 'description': 'Pashchim Medinipur_123_2017', 'priority': 100, 'creation_timestamp_ms': 1762657034045, 'update_timestamp_ms': 1762657034045, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'BH2QCAX4UY542XGJ3OTBL3UU', 'name': 'projects/ext-datasets/operations/BH2QCAX4UY542XGJ3OTBL3UU'}\n","Grid 124\n","curr_year 2017\n","Saving data for Pashchim Medinipur 2017\n","Task Started {'state': 'READY', 'description': 'Pashchim Medinipur_124_2017', 'priority': 100, 'creation_timestamp_ms': 1762657040222, 'update_timestamp_ms': 1762657040222, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'RFJGCG43WQHXWTEIZOO5PLHO', 'name': 'projects/ext-datasets/operations/RFJGCG43WQHXWTEIZOO5PLHO'}\n","Grid 125\n","curr_year 2017\n","Saving data for Pashchim Medinipur 2017\n","Task Started {'state': 'READY', 'description': 'Pashchim Medinipur_125_2017', 'priority': 100, 'creation_timestamp_ms': 1762657047717, 'update_timestamp_ms': 1762657047717, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'YZB4KBBV6YL77ZH2V5KYQVOH', 'name': 'projects/ext-datasets/operations/YZB4KBBV6YL77ZH2V5KYQVOH'}\n","Grid 126\n","curr_year 2017\n","Saving data for Pashchim Medinipur 2017\n","Task Started {'state': 'READY', 'description': 'Pashchim Medinipur_126_2017', 'priority': 100, 'creation_timestamp_ms': 1762657055564, 'update_timestamp_ms': 1762657055564, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'HN2YO27JJ655VQPWLRY3CKDJ', 'name': 'projects/ext-datasets/operations/HN2YO27JJ655VQPWLRY3CKDJ'}\n","Grid 127\n","curr_year 2017\n","Saving data for Pashchim Medinipur 2017\n","Task Started {'state': 'READY', 'description': 'Pashchim Medinipur_127_2017', 'priority': 100, 'creation_timestamp_ms': 1762657062509, 'update_timestamp_ms': 1762657062509, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'QH76BPVJSYGQCZRBOBQQCVB3', 'name': 'projects/ext-datasets/operations/QH76BPVJSYGQCZRBOBQQCVB3'}\n","Grid 128\n","curr_year 2017\n","Saving data for Pashchim Medinipur 2017\n","Task Started {'state': 'READY', 'description': 'Pashchim Medinipur_128_2017', 'priority': 100, 'creation_timestamp_ms': 1762657066784, 'update_timestamp_ms': 1762657066784, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'FY46YSUR3GJO6EVU7JRAQEY2', 'name': 'projects/ext-datasets/operations/FY46YSUR3GJO6EVU7JRAQEY2'}\n","Grid 129\n","curr_year 2017\n","Saving data for Pashchim Medinipur 2017\n","Task Started {'state': 'READY', 'description': 'Pashchim Medinipur_129_2017', 'priority': 100, 'creation_timestamp_ms': 1762657075843, 'update_timestamp_ms': 1762657075843, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'DCZM2OIBC4XWFCHZTL4C3Z2N', 'name': 'projects/ext-datasets/operations/DCZM2OIBC4XWFCHZTL4C3Z2N'}\n","Grid 130\n","curr_year 2017\n","Saving data for Pashchim Medinipur 2017\n","Task Started {'state': 'READY', 'description': 'Pashchim Medinipur_130_2017', 'priority': 100, 'creation_timestamp_ms': 1762657079376, 'update_timestamp_ms': 1762657079376, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '4OEAXEP6MTWKSKGQ7DE2CD5Q', 'name': 'projects/ext-datasets/operations/4OEAXEP6MTWKSKGQ7DE2CD5Q'}\n","Grid 131\n","curr_year 2017\n","Saving data for Pashchim Medinipur 2017\n","Task Started {'state': 'READY', 'description': 'Pashchim Medinipur_131_2017', 'priority': 100, 'creation_timestamp_ms': 1762657089946, 'update_timestamp_ms': 1762657089946, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '4JWFYNHQ3X5H2KW6VY4PN57X', 'name': 'projects/ext-datasets/operations/4JWFYNHQ3X5H2KW6VY4PN57X'}\n","Grid 132\n","curr_year 2017\n","Saving data for Pashchim Medinipur 2017\n","Task Started {'state': 'READY', 'description': 'Pashchim Medinipur_132_2017', 'priority': 100, 'creation_timestamp_ms': 1762657096800, 'update_timestamp_ms': 1762657096800, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'NTWDLE6Q3BILPFQGJLO2SJIS', 'name': 'projects/ext-datasets/operations/NTWDLE6Q3BILPFQGJLO2SJIS'}\n","Grid 133\n","curr_year 2017\n","Saving data for Pashchim Medinipur 2017\n","Task Started {'state': 'READY', 'description': 'Pashchim Medinipur_133_2017', 'priority': 100, 'creation_timestamp_ms': 1762657102962, 'update_timestamp_ms': 1762657102962, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'SPSTNZV2PHA3NH7ELB23XKDV', 'name': 'projects/ext-datasets/operations/SPSTNZV2PHA3NH7ELB23XKDV'}\n","Grid 134\n","curr_year 2017\n","Saving data for Pashchim Medinipur 2017\n","Task Started {'state': 'READY', 'description': 'Pashchim Medinipur_134_2017', 'priority': 100, 'creation_timestamp_ms': 1762657108805, 'update_timestamp_ms': 1762657108805, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'AWG4QRGYMWFW2N67LXPFTQ46', 'name': 'projects/ext-datasets/operations/AWG4QRGYMWFW2N67LXPFTQ46'}\n","Grid 135\n","curr_year 2017\n","Saving data for Pashchim Medinipur 2017\n","Task Started {'state': 'READY', 'description': 'Pashchim Medinipur_135_2017', 'priority': 100, 'creation_timestamp_ms': 1762657116011, 'update_timestamp_ms': 1762657116011, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'BMREEUG36OXX7BDXOLY7IRCX', 'name': 'projects/ext-datasets/operations/BMREEUG36OXX7BDXOLY7IRCX'}\n","Grid 136\n","curr_year 2017\n","Saving data for Pashchim Medinipur 2017\n","Task Started {'state': 'READY', 'description': 'Pashchim Medinipur_136_2017', 'priority': 100, 'creation_timestamp_ms': 1762657127187, 'update_timestamp_ms': 1762657127187, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'MB5NLIHHLXPL47HCZXG5ZVP6', 'name': 'projects/ext-datasets/operations/MB5NLIHHLXPL47HCZXG5ZVP6'}\n","7580.883316516876\n","Year 2017, District 24: Purba Medinipur, grids: 66\n","Grid 0\n","curr_year 2017\n","Saving data for Purba Medinipur 2017\n","Task Started {'state': 'READY', 'description': 'Purba Medinipur_0_2017', 'priority': 100, 'creation_timestamp_ms': 1762657143044, 'update_timestamp_ms': 1762657143044, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'NFKNSCZCXX6NWFUEEIA23I6P', 'name': 'projects/ext-datasets/operations/NFKNSCZCXX6NWFUEEIA23I6P'}\n","Grid 1\n","curr_year 2017\n","Saving data for Purba Medinipur 2017\n","Task Started {'state': 'READY', 'description': 'Purba Medinipur_1_2017', 'priority': 100, 'creation_timestamp_ms': 1762657149907, 'update_timestamp_ms': 1762657149907, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'X3LL7FUYRIIXVHMNEYJLIEL4', 'name': 'projects/ext-datasets/operations/X3LL7FUYRIIXVHMNEYJLIEL4'}\n","Grid 2\n","curr_year 2017\n","Saving data for Purba Medinipur 2017\n","Task Started {'state': 'READY', 'description': 'Purba Medinipur_2_2017', 'priority': 100, 'creation_timestamp_ms': 1762657157370, 'update_timestamp_ms': 1762657157370, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'EYL36YDY2FGDLEFWNAAL7FNX', 'name': 'projects/ext-datasets/operations/EYL36YDY2FGDLEFWNAAL7FNX'}\n","Grid 3\n","curr_year 2017\n","Saving data for Purba Medinipur 2017\n","Task Started {'state': 'READY', 'description': 'Purba Medinipur_3_2017', 'priority': 100, 'creation_timestamp_ms': 1762657165718, 'update_timestamp_ms': 1762657165718, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'VHZAWT6Y4OZESX3S4MI3OYI7', 'name': 'projects/ext-datasets/operations/VHZAWT6Y4OZESX3S4MI3OYI7'}\n","Grid 4\n","curr_year 2017\n","Saving data for Purba Medinipur 2017\n","Task Started {'state': 'READY', 'description': 'Purba Medinipur_4_2017', 'priority': 100, 'creation_timestamp_ms': 1762657172956, 'update_timestamp_ms': 1762657172956, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'TLT5VBL3NCFFLCC5RPKIGZLB', 'name': 'projects/ext-datasets/operations/TLT5VBL3NCFFLCC5RPKIGZLB'}\n","Grid 5\n","curr_year 2017\n","Saving data for Purba Medinipur 2017\n","Task Started {'state': 'READY', 'description': 'Purba Medinipur_5_2017', 'priority': 100, 'creation_timestamp_ms': 1762657179898, 'update_timestamp_ms': 1762657179898, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'HFXM6IK3DPR5BYCNCTPG7CIV', 'name': 'projects/ext-datasets/operations/HFXM6IK3DPR5BYCNCTPG7CIV'}\n","Grid 6\n","curr_year 2017\n","Saving data for Purba Medinipur 2017\n","Task Started {'state': 'READY', 'description': 'Purba Medinipur_6_2017', 'priority': 100, 'creation_timestamp_ms': 1762657189258, 'update_timestamp_ms': 1762657189258, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'YRYOEJG7PEYVYP3IXVBD6QMH', 'name': 'projects/ext-datasets/operations/YRYOEJG7PEYVYP3IXVBD6QMH'}\n","Grid 7\n","curr_year 2017\n","Saving data for Purba Medinipur 2017\n","Task Started {'state': 'READY', 'description': 'Purba Medinipur_7_2017', 'priority': 100, 'creation_timestamp_ms': 1762657195636, 'update_timestamp_ms': 1762657195636, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'UNRTKAJKC7LWJ64MAEUPGGQQ', 'name': 'projects/ext-datasets/operations/UNRTKAJKC7LWJ64MAEUPGGQQ'}\n","Grid 8\n","curr_year 2017\n","Saving data for Purba Medinipur 2017\n","Task Started {'state': 'READY', 'description': 'Purba Medinipur_8_2017', 'priority': 100, 'creation_timestamp_ms': 1762657199840, 'update_timestamp_ms': 1762657199840, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'NXTYHOQ6L5ZG5VQFLWRNXUHV', 'name': 'projects/ext-datasets/operations/NXTYHOQ6L5ZG5VQFLWRNXUHV'}\n","Grid 9\n","curr_year 2017\n","Saving data for Purba Medinipur 2017\n","Task Started {'state': 'READY', 'description': 'Purba Medinipur_9_2017', 'priority': 100, 'creation_timestamp_ms': 1762657208523, 'update_timestamp_ms': 1762657208523, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'HLOI6D76NAR6CKRMMZZNPYK3', 'name': 'projects/ext-datasets/operations/HLOI6D76NAR6CKRMMZZNPYK3'}\n","Grid 10\n","curr_year 2017\n","Saving data for Purba Medinipur 2017\n","Task Started {'state': 'READY', 'description': 'Purba Medinipur_10_2017', 'priority': 100, 'creation_timestamp_ms': 1762657213022, 'update_timestamp_ms': 1762657213022, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '5CPS6XPVWQOQX3YPE5QJR34O', 'name': 'projects/ext-datasets/operations/5CPS6XPVWQOQX3YPE5QJR34O'}\n","Grid 11\n","curr_year 2017\n","Saving data for Purba Medinipur 2017\n","Task Started {'state': 'READY', 'description': 'Purba Medinipur_11_2017', 'priority': 100, 'creation_timestamp_ms': 1762657218824, 'update_timestamp_ms': 1762657218824, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'QI2VU5JGZ26EGOXYFDZUCPNN', 'name': 'projects/ext-datasets/operations/QI2VU5JGZ26EGOXYFDZUCPNN'}\n","Grid 12\n","curr_year 2017\n","Saving data for Purba Medinipur 2017\n","Task Started {'state': 'READY', 'description': 'Purba Medinipur_12_2017', 'priority': 100, 'creation_timestamp_ms': 1762657227317, 'update_timestamp_ms': 1762657227317, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'XN65OGOBPUAGWGXVCUP5XIZC', 'name': 'projects/ext-datasets/operations/XN65OGOBPUAGWGXVCUP5XIZC'}\n","Grid 13\n","curr_year 2017\n","Saving data for Purba Medinipur 2017\n","Task Started {'state': 'READY', 'description': 'Purba Medinipur_13_2017', 'priority': 100, 'creation_timestamp_ms': 1762657233690, 'update_timestamp_ms': 1762657233690, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'WNCVRHZJ2HA4ZUJ3XNFK7M6A', 'name': 'projects/ext-datasets/operations/WNCVRHZJ2HA4ZUJ3XNFK7M6A'}\n","Grid 14\n","curr_year 2017\n","Saving data for Purba Medinipur 2017\n","Task Started {'state': 'READY', 'description': 'Purba Medinipur_14_2017', 'priority': 100, 'creation_timestamp_ms': 1762657239566, 'update_timestamp_ms': 1762657239566, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'SHA377MFE4VLLNTC7BCJK6X5', 'name': 'projects/ext-datasets/operations/SHA377MFE4VLLNTC7BCJK6X5'}\n","Grid 15\n","curr_year 2017\n","Saving data for Purba Medinipur 2017\n","Task Started {'state': 'READY', 'description': 'Purba Medinipur_15_2017', 'priority': 100, 'creation_timestamp_ms': 1762657244254, 'update_timestamp_ms': 1762657244254, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '7HCKJKLFG27BJ6XQQKERHD3W', 'name': 'projects/ext-datasets/operations/7HCKJKLFG27BJ6XQQKERHD3W'}\n","Grid 16\n","curr_year 2017\n","Saving data for Purba Medinipur 2017\n","Task Started {'state': 'READY', 'description': 'Purba Medinipur_16_2017', 'priority': 100, 'creation_timestamp_ms': 1762657248546, 'update_timestamp_ms': 1762657248546, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '2OL4T4ZXUFO5EN3TO6EHXAY2', 'name': 'projects/ext-datasets/operations/2OL4T4ZXUFO5EN3TO6EHXAY2'}\n","Grid 17\n","curr_year 2017\n","Saving data for Purba Medinipur 2017\n","Task Started {'state': 'READY', 'description': 'Purba Medinipur_17_2017', 'priority': 100, 'creation_timestamp_ms': 1762657252318, 'update_timestamp_ms': 1762657252318, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'S3R53I57CP6FFMPDSJURSPLU', 'name': 'projects/ext-datasets/operations/S3R53I57CP6FFMPDSJURSPLU'}\n","Grid 18\n","curr_year 2017\n","Saving data for Purba Medinipur 2017\n","Task Started {'state': 'READY', 'description': 'Purba Medinipur_18_2017', 'priority': 100, 'creation_timestamp_ms': 1762657259317, 'update_timestamp_ms': 1762657259317, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '4QCUGOYVM4AODWF4JSBNRK6H', 'name': 'projects/ext-datasets/operations/4QCUGOYVM4AODWF4JSBNRK6H'}\n","Grid 19\n","curr_year 2017\n","Saving data for Purba Medinipur 2017\n","Task Started {'state': 'READY', 'description': 'Purba Medinipur_19_2017', 'priority': 100, 'creation_timestamp_ms': 1762657266873, 'update_timestamp_ms': 1762657266873, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'PQ7JQWSHIZSXNU3KHRWV226V', 'name': 'projects/ext-datasets/operations/PQ7JQWSHIZSXNU3KHRWV226V'}\n","Grid 20\n","curr_year 2017\n","Saving data for Purba Medinipur 2017\n","Task Started {'state': 'READY', 'description': 'Purba Medinipur_20_2017', 'priority': 100, 'creation_timestamp_ms': 1762657274261, 'update_timestamp_ms': 1762657274261, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'HR64SO7UJ3ZQIABZDZMTXN2S', 'name': 'projects/ext-datasets/operations/HR64SO7UJ3ZQIABZDZMTXN2S'}\n","Grid 21\n","curr_year 2017\n","Saving data for Purba Medinipur 2017\n","Task Started {'state': 'READY', 'description': 'Purba Medinipur_21_2017', 'priority': 100, 'creation_timestamp_ms': 1762657283377, 'update_timestamp_ms': 1762657283377, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'NCMIUWATTM2MQ47PNU4QLQGX', 'name': 'projects/ext-datasets/operations/NCMIUWATTM2MQ47PNU4QLQGX'}\n","Grid 22\n","curr_year 2017\n","Saving data for Purba Medinipur 2017\n","Task Started {'state': 'READY', 'description': 'Purba Medinipur_22_2017', 'priority': 100, 'creation_timestamp_ms': 1762657289088, 'update_timestamp_ms': 1762657289088, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'QHNNO2ELSV4WI6EG7PS5RTSV', 'name': 'projects/ext-datasets/operations/QHNNO2ELSV4WI6EG7PS5RTSV'}\n","Grid 23\n","curr_year 2017\n","Saving data for Purba Medinipur 2017\n","Task Started {'state': 'READY', 'description': 'Purba Medinipur_23_2017', 'priority': 100, 'creation_timestamp_ms': 1762657296286, 'update_timestamp_ms': 1762657296286, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'VH33RAYDODUAOACTX5VDFGXM', 'name': 'projects/ext-datasets/operations/VH33RAYDODUAOACTX5VDFGXM'}\n","Grid 24\n","curr_year 2017\n","Saving data for Purba Medinipur 2017\n","Task Started {'state': 'READY', 'description': 'Purba Medinipur_24_2017', 'priority': 100, 'creation_timestamp_ms': 1762657303274, 'update_timestamp_ms': 1762657303274, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'L6ZWWHFMMA6XQVOI4TPK2R6A', 'name': 'projects/ext-datasets/operations/L6ZWWHFMMA6XQVOI4TPK2R6A'}\n","Grid 25\n","curr_year 2017\n","Saving data for Purba Medinipur 2017\n","Task Started {'state': 'READY', 'description': 'Purba Medinipur_25_2017', 'priority': 100, 'creation_timestamp_ms': 1762657312339, 'update_timestamp_ms': 1762657312339, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'JOS6MHIGUDMJK6AG36XO7CGN', 'name': 'projects/ext-datasets/operations/JOS6MHIGUDMJK6AG36XO7CGN'}\n","Grid 26\n","curr_year 2017\n","Saving data for Purba Medinipur 2017\n","Task Started {'state': 'READY', 'description': 'Purba Medinipur_26_2017', 'priority': 100, 'creation_timestamp_ms': 1762657320926, 'update_timestamp_ms': 1762657320926, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'RSUGU6BKDPJE2JEPESARW2PS', 'name': 'projects/ext-datasets/operations/RSUGU6BKDPJE2JEPESARW2PS'}\n","Grid 27\n","curr_year 2017\n","Saving data for Purba Medinipur 2017\n","Task Started {'state': 'READY', 'description': 'Purba Medinipur_27_2017', 'priority': 100, 'creation_timestamp_ms': 1762657328345, 'update_timestamp_ms': 1762657328345, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'QDRLKNCE35GCMASX2R2VOAJD', 'name': 'projects/ext-datasets/operations/QDRLKNCE35GCMASX2R2VOAJD'}\n","Grid 28\n","curr_year 2017\n","Saving data for Purba Medinipur 2017\n","Task Started {'state': 'READY', 'description': 'Purba Medinipur_28_2017', 'priority': 100, 'creation_timestamp_ms': 1762657334358, 'update_timestamp_ms': 1762657334358, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'YE7AYLCHK2EAFOT2JMZ2PYUQ', 'name': 'projects/ext-datasets/operations/YE7AYLCHK2EAFOT2JMZ2PYUQ'}\n","Grid 29\n","curr_year 2017\n","Saving data for Purba Medinipur 2017\n","Task Started {'state': 'READY', 'description': 'Purba Medinipur_29_2017', 'priority': 100, 'creation_timestamp_ms': 1762657341340, 'update_timestamp_ms': 1762657341340, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'L5JUS35KWHCPGC6WYRVEZ3NS', 'name': 'projects/ext-datasets/operations/L5JUS35KWHCPGC6WYRVEZ3NS'}\n","Grid 30\n","curr_year 2017\n","Saving data for Purba Medinipur 2017\n","Task Started {'state': 'READY', 'description': 'Purba Medinipur_30_2017', 'priority': 100, 'creation_timestamp_ms': 1762657349275, 'update_timestamp_ms': 1762657349275, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'MKQY5NBG44JJ5QRVZHRPA2B4', 'name': 'projects/ext-datasets/operations/MKQY5NBG44JJ5QRVZHRPA2B4'}\n","Grid 31\n","curr_year 2017\n","Saving data for Purba Medinipur 2017\n","Task Started {'state': 'READY', 'description': 'Purba Medinipur_31_2017', 'priority': 100, 'creation_timestamp_ms': 1762657356682, 'update_timestamp_ms': 1762657356682, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '3X447YFUNHEHIMOFNAWBXMJJ', 'name': 'projects/ext-datasets/operations/3X447YFUNHEHIMOFNAWBXMJJ'}\n","Grid 32\n","curr_year 2017\n","Saving data for Purba Medinipur 2017\n","Task Started {'state': 'READY', 'description': 'Purba Medinipur_32_2017', 'priority': 100, 'creation_timestamp_ms': 1762657363331, 'update_timestamp_ms': 1762657363331, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '2KJETJDPKLZJBSW772CLXJQA', 'name': 'projects/ext-datasets/operations/2KJETJDPKLZJBSW772CLXJQA'}\n","Grid 33\n","curr_year 2017\n","Saving data for Purba Medinipur 2017\n","Task Started {'state': 'READY', 'description': 'Purba Medinipur_33_2017', 'priority': 100, 'creation_timestamp_ms': 1762657368275, 'update_timestamp_ms': 1762657368275, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '7FMTES43YOXVLO3EKXN5M6CX', 'name': 'projects/ext-datasets/operations/7FMTES43YOXVLO3EKXN5M6CX'}\n","Grid 34\n","curr_year 2017\n","Saving data for Purba Medinipur 2017\n","Task Started {'state': 'READY', 'description': 'Purba Medinipur_34_2017', 'priority': 100, 'creation_timestamp_ms': 1762657376229, 'update_timestamp_ms': 1762657376229, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'NLRTYMEML47QL3MZLI5M7Y7A', 'name': 'projects/ext-datasets/operations/NLRTYMEML47QL3MZLI5M7Y7A'}\n","Grid 35\n","curr_year 2017\n","Saving data for Purba Medinipur 2017\n","Task Started {'state': 'READY', 'description': 'Purba Medinipur_35_2017', 'priority': 100, 'creation_timestamp_ms': 1762657383237, 'update_timestamp_ms': 1762657383237, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'AHFEMQK3ZRKL2NSHSP47OHK3', 'name': 'projects/ext-datasets/operations/AHFEMQK3ZRKL2NSHSP47OHK3'}\n","Grid 36\n","curr_year 2017\n","Saving data for Purba Medinipur 2017\n","Task Started {'state': 'READY', 'description': 'Purba Medinipur_36_2017', 'priority': 100, 'creation_timestamp_ms': 1762657389555, 'update_timestamp_ms': 1762657389555, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '5WUSEIDEOJWLMHCS2KADKWXC', 'name': 'projects/ext-datasets/operations/5WUSEIDEOJWLMHCS2KADKWXC'}\n","Grid 37\n","curr_year 2017\n","Saving data for Purba Medinipur 2017\n","Task Started {'state': 'READY', 'description': 'Purba Medinipur_37_2017', 'priority': 100, 'creation_timestamp_ms': 1762657397022, 'update_timestamp_ms': 1762657397022, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'YE3U3KFAKUL6DOJMDYCXZBHU', 'name': 'projects/ext-datasets/operations/YE3U3KFAKUL6DOJMDYCXZBHU'}\n","Grid 38\n","curr_year 2017\n","Saving data for Purba Medinipur 2017\n","Task Started {'state': 'READY', 'description': 'Purba Medinipur_38_2017', 'priority': 100, 'creation_timestamp_ms': 1762657405477, 'update_timestamp_ms': 1762657405477, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'XIMTQNVF3BZTESY35B5SJDTN', 'name': 'projects/ext-datasets/operations/XIMTQNVF3BZTESY35B5SJDTN'}\n","Grid 39\n","curr_year 2017\n","Saving data for Purba Medinipur 2017\n","Task Started {'state': 'READY', 'description': 'Purba Medinipur_39_2017', 'priority': 100, 'creation_timestamp_ms': 1762657413101, 'update_timestamp_ms': 1762657413101, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'NMY3CNFHLH2NQMM4F66PEMSI', 'name': 'projects/ext-datasets/operations/NMY3CNFHLH2NQMM4F66PEMSI'}\n","Grid 40\n","curr_year 2017\n","Saving data for Purba Medinipur 2017\n","Task Started {'state': 'READY', 'description': 'Purba Medinipur_40_2017', 'priority': 100, 'creation_timestamp_ms': 1762657421166, 'update_timestamp_ms': 1762657421166, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'A7TCPZ32MIPT3G7LXBOEXG63', 'name': 'projects/ext-datasets/operations/A7TCPZ32MIPT3G7LXBOEXG63'}\n","Grid 41\n","curr_year 2017\n","Saving data for Purba Medinipur 2017\n","Task Started {'state': 'READY', 'description': 'Purba Medinipur_41_2017', 'priority': 100, 'creation_timestamp_ms': 1762657428249, 'update_timestamp_ms': 1762657428249, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'W2NXGIOOMEQV4QIODVZ5FOBW', 'name': 'projects/ext-datasets/operations/W2NXGIOOMEQV4QIODVZ5FOBW'}\n","Grid 42\n","curr_year 2017\n","Saving data for Purba Medinipur 2017\n","Task Started {'state': 'READY', 'description': 'Purba Medinipur_42_2017', 'priority': 100, 'creation_timestamp_ms': 1762657436025, 'update_timestamp_ms': 1762657436025, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '64V3QFTQXEFBI3XVAYC3NHJN', 'name': 'projects/ext-datasets/operations/64V3QFTQXEFBI3XVAYC3NHJN'}\n","Grid 43\n","curr_year 2017\n","Saving data for Purba Medinipur 2017\n","Task Started {'state': 'READY', 'description': 'Purba Medinipur_43_2017', 'priority': 100, 'creation_timestamp_ms': 1762657443414, 'update_timestamp_ms': 1762657443414, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'VLVA4DDQOGFYDZ5WZJP2UDAJ', 'name': 'projects/ext-datasets/operations/VLVA4DDQOGFYDZ5WZJP2UDAJ'}\n","Grid 44\n","curr_year 2017\n","Saving data for Purba Medinipur 2017\n","Task Started {'state': 'READY', 'description': 'Purba Medinipur_44_2017', 'priority': 100, 'creation_timestamp_ms': 1762657451191, 'update_timestamp_ms': 1762657451191, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '7NYB47PM4MVRZ6NIPA5WORN4', 'name': 'projects/ext-datasets/operations/7NYB47PM4MVRZ6NIPA5WORN4'}\n","Grid 45\n","curr_year 2017\n","Saving data for Purba Medinipur 2017\n","Task Started {'state': 'READY', 'description': 'Purba Medinipur_45_2017', 'priority': 100, 'creation_timestamp_ms': 1762657459746, 'update_timestamp_ms': 1762657459746, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'LUU6WH2SK7PDN4PK3CHEDO3M', 'name': 'projects/ext-datasets/operations/LUU6WH2SK7PDN4PK3CHEDO3M'}\n","Grid 46\n","curr_year 2017\n","Saving data for Purba Medinipur 2017\n","Task Started {'state': 'READY', 'description': 'Purba Medinipur_46_2017', 'priority': 100, 'creation_timestamp_ms': 1762657466211, 'update_timestamp_ms': 1762657466211, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'UVS2QOYH2PLGTMFSHM3XMPL3', 'name': 'projects/ext-datasets/operations/UVS2QOYH2PLGTMFSHM3XMPL3'}\n","Grid 47\n","curr_year 2017\n","Saving data for Purba Medinipur 2017\n","Task Started {'state': 'READY', 'description': 'Purba Medinipur_47_2017', 'priority': 100, 'creation_timestamp_ms': 1762657474364, 'update_timestamp_ms': 1762657474364, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'HYMGFLYI3EUHTCRX2G4YSIBJ', 'name': 'projects/ext-datasets/operations/HYMGFLYI3EUHTCRX2G4YSIBJ'}\n","Grid 48\n","curr_year 2017\n","Saving data for Purba Medinipur 2017\n","Task Started {'state': 'READY', 'description': 'Purba Medinipur_48_2017', 'priority': 100, 'creation_timestamp_ms': 1762657481945, 'update_timestamp_ms': 1762657481945, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '4BN76EHEBBDZGQ36ZFRXQUQS', 'name': 'projects/ext-datasets/operations/4BN76EHEBBDZGQ36ZFRXQUQS'}\n","Grid 49\n","curr_year 2017\n","Saving data for Purba Medinipur 2017\n","Task Started {'state': 'READY', 'description': 'Purba Medinipur_49_2017', 'priority': 100, 'creation_timestamp_ms': 1762657486739, 'update_timestamp_ms': 1762657486739, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'G2QERUTI7ZNPL2SGP6BHQG3T', 'name': 'projects/ext-datasets/operations/G2QERUTI7ZNPL2SGP6BHQG3T'}\n","Grid 50\n","curr_year 2017\n","Saving data for Purba Medinipur 2017\n","Task Started {'state': 'READY', 'description': 'Purba Medinipur_50_2017', 'priority': 100, 'creation_timestamp_ms': 1762657492966, 'update_timestamp_ms': 1762657492966, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'GYW4IE7E6CWEFZ4W4PPYTQHN', 'name': 'projects/ext-datasets/operations/GYW4IE7E6CWEFZ4W4PPYTQHN'}\n","Grid 51\n","curr_year 2017\n","Saving data for Purba Medinipur 2017\n","Task Started {'state': 'READY', 'description': 'Purba Medinipur_51_2017', 'priority': 100, 'creation_timestamp_ms': 1762657501242, 'update_timestamp_ms': 1762657501242, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'STFV4MBGEI4IU5WJJV6FGP7M', 'name': 'projects/ext-datasets/operations/STFV4MBGEI4IU5WJJV6FGP7M'}\n","Grid 52\n","curr_year 2017\n","Saving data for Purba Medinipur 2017\n","Task Started {'state': 'READY', 'description': 'Purba Medinipur_52_2017', 'priority': 100, 'creation_timestamp_ms': 1762657508705, 'update_timestamp_ms': 1762657508705, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '7EJGN4K6ZQ5BKTGOKWOUADDC', 'name': 'projects/ext-datasets/operations/7EJGN4K6ZQ5BKTGOKWOUADDC'}\n","Grid 53\n","curr_year 2017\n","Saving data for Purba Medinipur 2017\n","Task Started {'state': 'READY', 'description': 'Purba Medinipur_53_2017', 'priority': 100, 'creation_timestamp_ms': 1762657517426, 'update_timestamp_ms': 1762657517426, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'MGHCS3PDQ43H5AWWB7ZLYMAR', 'name': 'projects/ext-datasets/operations/MGHCS3PDQ43H5AWWB7ZLYMAR'}\n","Grid 54\n","curr_year 2017\n","Saving data for Purba Medinipur 2017\n","Task Started {'state': 'READY', 'description': 'Purba Medinipur_54_2017', 'priority': 100, 'creation_timestamp_ms': 1762657523343, 'update_timestamp_ms': 1762657523343, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'ECSDKPVOKNRGOWKRPHD6YQO2', 'name': 'projects/ext-datasets/operations/ECSDKPVOKNRGOWKRPHD6YQO2'}\n","Grid 55\n","curr_year 2017\n","Saving data for Purba Medinipur 2017\n","Task Started {'state': 'READY', 'description': 'Purba Medinipur_55_2017', 'priority': 100, 'creation_timestamp_ms': 1762657531327, 'update_timestamp_ms': 1762657531327, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'HTBG5GMB44VGTI2DQMDFAUHQ', 'name': 'projects/ext-datasets/operations/HTBG5GMB44VGTI2DQMDFAUHQ'}\n","Grid 56\n","curr_year 2017\n","Saving data for Purba Medinipur 2017\n","Task Started {'state': 'READY', 'description': 'Purba Medinipur_56_2017', 'priority': 100, 'creation_timestamp_ms': 1762657539664, 'update_timestamp_ms': 1762657539664, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'UTURDEYASPPGZ2JAKQIWVTX6', 'name': 'projects/ext-datasets/operations/UTURDEYASPPGZ2JAKQIWVTX6'}\n","Grid 57\n","curr_year 2017\n","Saving data for Purba Medinipur 2017\n","Task Started {'state': 'READY', 'description': 'Purba Medinipur_57_2017', 'priority': 100, 'creation_timestamp_ms': 1762657547246, 'update_timestamp_ms': 1762657547246, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'T3L2GNWKIVO6OA64TECF5IFC', 'name': 'projects/ext-datasets/operations/T3L2GNWKIVO6OA64TECF5IFC'}\n","Grid 58\n","curr_year 2017\n","Saving data for Purba Medinipur 2017\n","Task Started {'state': 'READY', 'description': 'Purba Medinipur_58_2017', 'priority': 100, 'creation_timestamp_ms': 1762657551131, 'update_timestamp_ms': 1762657551131, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '2LPEF26DVQLNIFSGD6HIT25S', 'name': 'projects/ext-datasets/operations/2LPEF26DVQLNIFSGD6HIT25S'}\n","Grid 59\n","curr_year 2017\n","Saving data for Purba Medinipur 2017\n","Task Started {'state': 'READY', 'description': 'Purba Medinipur_59_2017', 'priority': 100, 'creation_timestamp_ms': 1762657559342, 'update_timestamp_ms': 1762657559342, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'WLGCU5XBQGP6MXJDOXWER3V6', 'name': 'projects/ext-datasets/operations/WLGCU5XBQGP6MXJDOXWER3V6'}\n","Grid 60\n","curr_year 2017\n","Saving data for Purba Medinipur 2017\n","Task Started {'state': 'READY', 'description': 'Purba Medinipur_60_2017', 'priority': 100, 'creation_timestamp_ms': 1762657572975, 'update_timestamp_ms': 1762657572975, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'GOEHV2ZA4X664FMUNO62HUM5', 'name': 'projects/ext-datasets/operations/GOEHV2ZA4X664FMUNO62HUM5'}\n","Grid 61\n","curr_year 2017\n","Saving data for Purba Medinipur 2017\n","Task Started {'state': 'READY', 'description': 'Purba Medinipur_61_2017', 'priority': 100, 'creation_timestamp_ms': 1762657580419, 'update_timestamp_ms': 1762657580419, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'ACJYYPK7NQXZNZUHTBICPG42', 'name': 'projects/ext-datasets/operations/ACJYYPK7NQXZNZUHTBICPG42'}\n","Grid 62\n","curr_year 2017\n","Saving data for Purba Medinipur 2017\n","Task Started {'state': 'READY', 'description': 'Purba Medinipur_62_2017', 'priority': 100, 'creation_timestamp_ms': 1762657584248, 'update_timestamp_ms': 1762657584248, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '2HHKDJSRNZMEI5KMBDLFFWD5', 'name': 'projects/ext-datasets/operations/2HHKDJSRNZMEI5KMBDLFFWD5'}\n","Grid 63\n","curr_year 2017\n","Saving data for Purba Medinipur 2017\n","Task Started {'state': 'READY', 'description': 'Purba Medinipur_63_2017', 'priority': 100, 'creation_timestamp_ms': 1762657591412, 'update_timestamp_ms': 1762657591412, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'F2B2IYVKSVYZ56Y7WV6XHBI2', 'name': 'projects/ext-datasets/operations/F2B2IYVKSVYZ56Y7WV6XHBI2'}\n","Grid 64\n","curr_year 2017\n","Saving data for Purba Medinipur 2017\n","Task Started {'state': 'READY', 'description': 'Purba Medinipur_64_2017', 'priority': 100, 'creation_timestamp_ms': 1762657598365, 'update_timestamp_ms': 1762657598365, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'Q5TXVMHH6QL5F3EVHOZIB2QZ', 'name': 'projects/ext-datasets/operations/Q5TXVMHH6QL5F3EVHOZIB2QZ'}\n","Grid 65\n","curr_year 2017\n","Saving data for Purba Medinipur 2017\n","Task Started {'state': 'READY', 'description': 'Purba Medinipur_65_2017', 'priority': 100, 'creation_timestamp_ms': 1762657605363, 'update_timestamp_ms': 1762657605363, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'AZUGC6PKQR6AFOA4ID5ELHTL', 'name': 'projects/ext-datasets/operations/AZUGC6PKQR6AFOA4ID5ELHTL'}\n","8059.015738725662\n","Year 2017, District 25: Puruliya, grids: 14\n","Grid 0\n","curr_year 2017\n","Saving data for Puruliya 2017\n","Task Started {'state': 'READY', 'description': 'Puruliya_0_2017', 'priority': 100, 'creation_timestamp_ms': 1762657617740, 'update_timestamp_ms': 1762657617740, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '5M4FOJF74A6FYVNEWN7QBZRV', 'name': 'projects/ext-datasets/operations/5M4FOJF74A6FYVNEWN7QBZRV'}\n","Grid 1\n","curr_year 2017\n","Saving data for Puruliya 2017\n","Task Started {'state': 'READY', 'description': 'Puruliya_1_2017', 'priority': 100, 'creation_timestamp_ms': 1762657627843, 'update_timestamp_ms': 1762657627843, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'V4P6PUJOLKOGAWBVRANK67ES', 'name': 'projects/ext-datasets/operations/V4P6PUJOLKOGAWBVRANK67ES'}\n","Grid 2\n","curr_year 2017\n","Saving data for Puruliya 2017\n","Task Started {'state': 'READY', 'description': 'Puruliya_2_2017', 'priority': 100, 'creation_timestamp_ms': 1762657633527, 'update_timestamp_ms': 1762657633527, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'CHHXOWHFOOSQY6C5DKEY77JN', 'name': 'projects/ext-datasets/operations/CHHXOWHFOOSQY6C5DKEY77JN'}\n","Grid 3\n","curr_year 2017\n","Saving data for Puruliya 2017\n","Task Started {'state': 'READY', 'description': 'Puruliya_3_2017', 'priority': 100, 'creation_timestamp_ms': 1762657638867, 'update_timestamp_ms': 1762657638867, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'IBFQ6N35DOGLCCN5G5EBIKTP', 'name': 'projects/ext-datasets/operations/IBFQ6N35DOGLCCN5G5EBIKTP'}\n","Grid 4\n","curr_year 2017\n","Saving data for Puruliya 2017\n","Task Started {'state': 'READY', 'description': 'Puruliya_4_2017', 'priority': 100, 'creation_timestamp_ms': 1762657646397, 'update_timestamp_ms': 1762657646397, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'ITON6E27PXDBKJCJ5DJXWXLH', 'name': 'projects/ext-datasets/operations/ITON6E27PXDBKJCJ5DJXWXLH'}\n","Grid 5\n","curr_year 2017\n","Saving data for Puruliya 2017\n","Task Started {'state': 'READY', 'description': 'Puruliya_5_2017', 'priority': 100, 'creation_timestamp_ms': 1762657654320, 'update_timestamp_ms': 1762657654320, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'M253CVPDA5FTXKXKP5FKOXEH', 'name': 'projects/ext-datasets/operations/M253CVPDA5FTXKXKP5FKOXEH'}\n","Grid 6\n","curr_year 2017\n","Saving data for Puruliya 2017\n","Task Started {'state': 'READY', 'description': 'Puruliya_6_2017', 'priority': 100, 'creation_timestamp_ms': 1762657662112, 'update_timestamp_ms': 1762657662112, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'IDACHDF74JE7SJFBRCGNOS7A', 'name': 'projects/ext-datasets/operations/IDACHDF74JE7SJFBRCGNOS7A'}\n","Grid 7\n","curr_year 2017\n","Saving data for Puruliya 2017\n","Task Started {'state': 'READY', 'description': 'Puruliya_7_2017', 'priority': 100, 'creation_timestamp_ms': 1762657668483, 'update_timestamp_ms': 1762657668483, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '7C6PQT5WVKHTOT7FYOPWBOGQ', 'name': 'projects/ext-datasets/operations/7C6PQT5WVKHTOT7FYOPWBOGQ'}\n","Grid 8\n","curr_year 2017\n","Saving data for Puruliya 2017\n","Task Started {'state': 'READY', 'description': 'Puruliya_8_2017', 'priority': 100, 'creation_timestamp_ms': 1762657674895, 'update_timestamp_ms': 1762657674895, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'LAY5XYLYOEAGFFD67X2CLZS7', 'name': 'projects/ext-datasets/operations/LAY5XYLYOEAGFFD67X2CLZS7'}\n","Grid 9\n","curr_year 2017\n","Saving data for Puruliya 2017\n","Task Started {'state': 'READY', 'description': 'Puruliya_9_2017', 'priority': 100, 'creation_timestamp_ms': 1762657683603, 'update_timestamp_ms': 1762657683603, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '57YVND4GM65EWUX5C3SKUTGY', 'name': 'projects/ext-datasets/operations/57YVND4GM65EWUX5C3SKUTGY'}\n","Grid 10\n","curr_year 2017\n","Saving data for Puruliya 2017\n","Task Started {'state': 'READY', 'description': 'Puruliya_10_2017', 'priority': 100, 'creation_timestamp_ms': 1762657690623, 'update_timestamp_ms': 1762657690623, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '4NWEGVDZQ7XZKA75ZJ6JEVBB', 'name': 'projects/ext-datasets/operations/4NWEGVDZQ7XZKA75ZJ6JEVBB'}\n","Grid 11\n","curr_year 2017\n","Saving data for Puruliya 2017\n","Task Started {'state': 'READY', 'description': 'Puruliya_11_2017', 'priority': 100, 'creation_timestamp_ms': 1762657697680, 'update_timestamp_ms': 1762657697680, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'XJR3AEJSUWFQVS2AC5S3L7SE', 'name': 'projects/ext-datasets/operations/XJR3AEJSUWFQVS2AC5S3L7SE'}\n","Grid 12\n","curr_year 2017\n","Saving data for Puruliya 2017\n","Task Started {'state': 'READY', 'description': 'Puruliya_12_2017', 'priority': 100, 'creation_timestamp_ms': 1762657703789, 'update_timestamp_ms': 1762657703789, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'DINWXLEKJUU2VZWR6AACYX7T', 'name': 'projects/ext-datasets/operations/DINWXLEKJUU2VZWR6AACYX7T'}\n","Grid 13\n","curr_year 2017\n","Saving data for Puruliya 2017\n","Task Started {'state': 'READY', 'description': 'Puruliya_13_2017', 'priority': 100, 'creation_timestamp_ms': 1762657710134, 'update_timestamp_ms': 1762657710134, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'VOFZSRN2Q24F3TWJNTGQSRZR', 'name': 'projects/ext-datasets/operations/VOFZSRN2Q24F3TWJNTGQSRZR'}\n","8163.815611839294\n","Year 2017, District 26: South 24 Parganas, grids: 96\n","Grid 0\n","curr_year 2017\n","Saving data for South 24 Parganas 2017\n","Task Started {'state': 'READY', 'description': 'South 24 Parganas_0_2017', 'priority': 100, 'creation_timestamp_ms': 1762657721617, 'update_timestamp_ms': 1762657721617, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'CO7CVHBLECLP5O3BCXI2VNPK', 'name': 'projects/ext-datasets/operations/CO7CVHBLECLP5O3BCXI2VNPK'}\n","Grid 1\n","curr_year 2017\n","Saving data for South 24 Parganas 2017\n","Task Started {'state': 'READY', 'description': 'South 24 Parganas_1_2017', 'priority': 100, 'creation_timestamp_ms': 1762657726433, 'update_timestamp_ms': 1762657726433, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '2H5I2G4GW4K5S4R4RKRLVOAO', 'name': 'projects/ext-datasets/operations/2H5I2G4GW4K5S4R4RKRLVOAO'}\n","Grid 2\n","curr_year 2017\n","Saving data for South 24 Parganas 2017\n","Task Started {'state': 'READY', 'description': 'South 24 Parganas_2_2017', 'priority': 100, 'creation_timestamp_ms': 1762657736622, 'update_timestamp_ms': 1762657736622, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '2WIJVNRWZLDJJGSV7VJ6SNWV', 'name': 'projects/ext-datasets/operations/2WIJVNRWZLDJJGSV7VJ6SNWV'}\n","Grid 3\n","curr_year 2017\n","Saving data for South 24 Parganas 2017\n","Task Started {'state': 'READY', 'description': 'South 24 Parganas_3_2017', 'priority': 100, 'creation_timestamp_ms': 1762657743457, 'update_timestamp_ms': 1762657743457, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '5PTR3G7WQSO5C4AQD2UYV6OC', 'name': 'projects/ext-datasets/operations/5PTR3G7WQSO5C4AQD2UYV6OC'}\n","Grid 4\n","curr_year 2017\n","Saving data for South 24 Parganas 2017\n","Task Started {'state': 'READY', 'description': 'South 24 Parganas_4_2017', 'priority': 100, 'creation_timestamp_ms': 1762657749135, 'update_timestamp_ms': 1762657749135, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'ZAZWNY6SF5H5QTLV2W3FX6AD', 'name': 'projects/ext-datasets/operations/ZAZWNY6SF5H5QTLV2W3FX6AD'}\n","Grid 5\n","curr_year 2017\n","Saving data for South 24 Parganas 2017\n","Task Started {'state': 'READY', 'description': 'South 24 Parganas_5_2017', 'priority': 100, 'creation_timestamp_ms': 1762657760790, 'update_timestamp_ms': 1762657760790, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '62EE5QGJXR4TJKLPXQSSFEVQ', 'name': 'projects/ext-datasets/operations/62EE5QGJXR4TJKLPXQSSFEVQ'}\n","Grid 6\n","curr_year 2017\n","Saving data for South 24 Parganas 2017\n","Task Started {'state': 'READY', 'description': 'South 24 Parganas_6_2017', 'priority': 100, 'creation_timestamp_ms': 1762657774826, 'update_timestamp_ms': 1762657774826, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'Y3MW4Z3OJWSOEBFODVQGLMEN', 'name': 'projects/ext-datasets/operations/Y3MW4Z3OJWSOEBFODVQGLMEN'}\n","Grid 7\n","curr_year 2017\n","Saving data for South 24 Parganas 2017\n","Task Started {'state': 'READY', 'description': 'South 24 Parganas_7_2017', 'priority': 100, 'creation_timestamp_ms': 1762657786809, 'update_timestamp_ms': 1762657786809, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'FZBZWJY5HIQ6VHYO6IAXI2P4', 'name': 'projects/ext-datasets/operations/FZBZWJY5HIQ6VHYO6IAXI2P4'}\n","Grid 8\n","curr_year 2017\n","Saving data for South 24 Parganas 2017\n","Task Started {'state': 'READY', 'description': 'South 24 Parganas_8_2017', 'priority': 100, 'creation_timestamp_ms': 1762657794926, 'update_timestamp_ms': 1762657794926, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'BIIP6T4I7WSMPUBS3MLW7LF6', 'name': 'projects/ext-datasets/operations/BIIP6T4I7WSMPUBS3MLW7LF6'}\n","Grid 9\n","curr_year 2017\n","Saving data for South 24 Parganas 2017\n","Task Started {'state': 'READY', 'description': 'South 24 Parganas_9_2017', 'priority': 100, 'creation_timestamp_ms': 1762657806207, 'update_timestamp_ms': 1762657806207, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '2JG6KWR5AFWD2AYHRGX2O7PZ', 'name': 'projects/ext-datasets/operations/2JG6KWR5AFWD2AYHRGX2O7PZ'}\n","Grid 10\n","curr_year 2017\n","Saving data for South 24 Parganas 2017\n","Task Started {'state': 'READY', 'description': 'South 24 Parganas_10_2017', 'priority': 100, 'creation_timestamp_ms': 1762657815663, 'update_timestamp_ms': 1762657815663, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'XUAJECSKIE4BD3IVPXQI4J2S', 'name': 'projects/ext-datasets/operations/XUAJECSKIE4BD3IVPXQI4J2S'}\n","Grid 11\n","curr_year 2017\n","Saving data for South 24 Parganas 2017\n","Task Started {'state': 'READY', 'description': 'South 24 Parganas_11_2017', 'priority': 100, 'creation_timestamp_ms': 1762657825834, 'update_timestamp_ms': 1762657825834, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'XIFX4RYGD7KEQOZPW2M7Q2R6', 'name': 'projects/ext-datasets/operations/XIFX4RYGD7KEQOZPW2M7Q2R6'}\n","Grid 12\n","curr_year 2017\n","Saving data for South 24 Parganas 2017\n","Task Started {'state': 'READY', 'description': 'South 24 Parganas_12_2017', 'priority': 100, 'creation_timestamp_ms': 1762657836975, 'update_timestamp_ms': 1762657836975, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'BJBAMUYGWTH43MX5TH7KVZDW', 'name': 'projects/ext-datasets/operations/BJBAMUYGWTH43MX5TH7KVZDW'}\n","Grid 13\n","curr_year 2017\n","Saving data for South 24 Parganas 2017\n","Task Started {'state': 'READY', 'description': 'South 24 Parganas_13_2017', 'priority': 100, 'creation_timestamp_ms': 1762657846609, 'update_timestamp_ms': 1762657846609, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '2TQCMU7EHVXVHQL6HU7NF4QN', 'name': 'projects/ext-datasets/operations/2TQCMU7EHVXVHQL6HU7NF4QN'}\n","Grid 14\n","curr_year 2017\n","Saving data for South 24 Parganas 2017\n","Task Started {'state': 'READY', 'description': 'South 24 Parganas_14_2017', 'priority': 100, 'creation_timestamp_ms': 1762657853958, 'update_timestamp_ms': 1762657853958, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'LSVMZ4URZV2HPVRNQ3KZAT55', 'name': 'projects/ext-datasets/operations/LSVMZ4URZV2HPVRNQ3KZAT55'}\n","Grid 15\n","curr_year 2017\n","Saving data for South 24 Parganas 2017\n","Task Started {'state': 'READY', 'description': 'South 24 Parganas_15_2017', 'priority': 100, 'creation_timestamp_ms': 1762657862953, 'update_timestamp_ms': 1762657862953, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'PRP73XPBKBLQ4FKCGPNQVXB4', 'name': 'projects/ext-datasets/operations/PRP73XPBKBLQ4FKCGPNQVXB4'}\n","Grid 16\n","curr_year 2017\n","Saving data for South 24 Parganas 2017\n","Task Started {'state': 'READY', 'description': 'South 24 Parganas_16_2017', 'priority': 100, 'creation_timestamp_ms': 1762657872487, 'update_timestamp_ms': 1762657872487, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'IA6XNM7FRZNTIGHDLO63YYZA', 'name': 'projects/ext-datasets/operations/IA6XNM7FRZNTIGHDLO63YYZA'}\n","Grid 17\n","curr_year 2017\n","Saving data for South 24 Parganas 2017\n","Task Started {'state': 'READY', 'description': 'South 24 Parganas_17_2017', 'priority': 100, 'creation_timestamp_ms': 1762657883397, 'update_timestamp_ms': 1762657883397, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'NGZI7NSE6Q3RIUKLGVIB25NJ', 'name': 'projects/ext-datasets/operations/NGZI7NSE6Q3RIUKLGVIB25NJ'}\n","Grid 18\n","curr_year 2017\n","Saving data for South 24 Parganas 2017\n","Task Started {'state': 'READY', 'description': 'South 24 Parganas_18_2017', 'priority': 100, 'creation_timestamp_ms': 1762657894076, 'update_timestamp_ms': 1762657894076, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '7R7NGWVLTW5EGP6BHJYR7SXX', 'name': 'projects/ext-datasets/operations/7R7NGWVLTW5EGP6BHJYR7SXX'}\n","Grid 19\n","curr_year 2017\n","Saving data for South 24 Parganas 2017\n","Task Started {'state': 'READY', 'description': 'South 24 Parganas_19_2017', 'priority': 100, 'creation_timestamp_ms': 1762657905886, 'update_timestamp_ms': 1762657905886, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'UKD4UTDCYP27YHBC2BDV4EWL', 'name': 'projects/ext-datasets/operations/UKD4UTDCYP27YHBC2BDV4EWL'}\n","Grid 20\n","curr_year 2017\n","Saving data for South 24 Parganas 2017\n","Task Started {'state': 'READY', 'description': 'South 24 Parganas_20_2017', 'priority': 100, 'creation_timestamp_ms': 1762657914676, 'update_timestamp_ms': 1762657914676, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '35Z66UZU7LC5PTK6GMVJ6V6J', 'name': 'projects/ext-datasets/operations/35Z66UZU7LC5PTK6GMVJ6V6J'}\n","Grid 21\n","curr_year 2017\n","Saving data for South 24 Parganas 2017\n","Task Started {'state': 'READY', 'description': 'South 24 Parganas_21_2017', 'priority': 100, 'creation_timestamp_ms': 1762657924865, 'update_timestamp_ms': 1762657924865, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'D2BMBYJJZ2ZLVEARBZJML565', 'name': 'projects/ext-datasets/operations/D2BMBYJJZ2ZLVEARBZJML565'}\n","Grid 22\n","curr_year 2017\n","Saving data for South 24 Parganas 2017\n","Task Started {'state': 'READY', 'description': 'South 24 Parganas_22_2017', 'priority': 100, 'creation_timestamp_ms': 1762657941140, 'update_timestamp_ms': 1762657941140, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'S4K2JP3VDT5UKVFPI7HABIHR', 'name': 'projects/ext-datasets/operations/S4K2JP3VDT5UKVFPI7HABIHR'}\n","Grid 23\n","curr_year 2017\n","Saving data for South 24 Parganas 2017\n","Task Started {'state': 'READY', 'description': 'South 24 Parganas_23_2017', 'priority': 100, 'creation_timestamp_ms': 1762657949056, 'update_timestamp_ms': 1762657949056, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'IXOGIGFDDNUA72RRHV3ZLI3C', 'name': 'projects/ext-datasets/operations/IXOGIGFDDNUA72RRHV3ZLI3C'}\n","Grid 24\n","curr_year 2017\n","Saving data for South 24 Parganas 2017\n","Task Started {'state': 'READY', 'description': 'South 24 Parganas_24_2017', 'priority': 100, 'creation_timestamp_ms': 1762657954728, 'update_timestamp_ms': 1762657954728, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'XKRWEJTXI5TZCHJ5Q7FEEIEZ', 'name': 'projects/ext-datasets/operations/XKRWEJTXI5TZCHJ5Q7FEEIEZ'}\n","Grid 25\n","curr_year 2017\n","Saving data for South 24 Parganas 2017\n","Task Started {'state': 'READY', 'description': 'South 24 Parganas_25_2017', 'priority': 100, 'creation_timestamp_ms': 1762657967115, 'update_timestamp_ms': 1762657967115, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'IJ2FCKDETNQKZKF55VR5L6TM', 'name': 'projects/ext-datasets/operations/IJ2FCKDETNQKZKF55VR5L6TM'}\n","Grid 26\n","curr_year 2017\n","Saving data for South 24 Parganas 2017\n","Task Started {'state': 'READY', 'description': 'South 24 Parganas_26_2017', 'priority': 100, 'creation_timestamp_ms': 1762657977118, 'update_timestamp_ms': 1762657977118, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'PJW2WA7SH4TBRYBBBQY6KKBD', 'name': 'projects/ext-datasets/operations/PJW2WA7SH4TBRYBBBQY6KKBD'}\n","Grid 27\n","curr_year 2017\n","Saving data for South 24 Parganas 2017\n","Task Started {'state': 'READY', 'description': 'South 24 Parganas_27_2017', 'priority': 100, 'creation_timestamp_ms': 1762657982147, 'update_timestamp_ms': 1762657982147, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'YQR3742F6Y5NL53KSI56C4GC', 'name': 'projects/ext-datasets/operations/YQR3742F6Y5NL53KSI56C4GC'}\n","Grid 28\n","curr_year 2017\n","Saving data for South 24 Parganas 2017\n","Task Started {'state': 'READY', 'description': 'South 24 Parganas_28_2017', 'priority': 100, 'creation_timestamp_ms': 1762657993527, 'update_timestamp_ms': 1762657993527, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'C3GNJPDZHAKUU3V2MB2A7K2D', 'name': 'projects/ext-datasets/operations/C3GNJPDZHAKUU3V2MB2A7K2D'}\n","Grid 29\n","curr_year 2017\n","Saving data for South 24 Parganas 2017\n","Task Started {'state': 'READY', 'description': 'South 24 Parganas_29_2017', 'priority': 100, 'creation_timestamp_ms': 1762658004659, 'update_timestamp_ms': 1762658004659, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '2G2FEI4UX2Y5CVDRCFSYPYE4', 'name': 'projects/ext-datasets/operations/2G2FEI4UX2Y5CVDRCFSYPYE4'}\n","Grid 30\n","curr_year 2017\n","Saving data for South 24 Parganas 2017\n","Task Started {'state': 'READY', 'description': 'South 24 Parganas_30_2017', 'priority': 100, 'creation_timestamp_ms': 1762658015257, 'update_timestamp_ms': 1762658015257, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'WNGWXIIJZ63GHTKHWWJOQFPM', 'name': 'projects/ext-datasets/operations/WNGWXIIJZ63GHTKHWWJOQFPM'}\n","Grid 31\n","curr_year 2017\n","Saving data for South 24 Parganas 2017\n","Task Started {'state': 'READY', 'description': 'South 24 Parganas_31_2017', 'priority': 100, 'creation_timestamp_ms': 1762658023473, 'update_timestamp_ms': 1762658023473, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'NJ3JE2P2BHN2KRXTVR3PQ5HC', 'name': 'projects/ext-datasets/operations/NJ3JE2P2BHN2KRXTVR3PQ5HC'}\n","Grid 32\n","curr_year 2017\n","Saving data for South 24 Parganas 2017\n","Task Started {'state': 'READY', 'description': 'South 24 Parganas_32_2017', 'priority': 100, 'creation_timestamp_ms': 1762658028979, 'update_timestamp_ms': 1762658028979, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'QV6U3AYQ5F75G2CIJHVHSWDG', 'name': 'projects/ext-datasets/operations/QV6U3AYQ5F75G2CIJHVHSWDG'}\n","Grid 33\n","curr_year 2017\n","Saving data for South 24 Parganas 2017\n","Task Started {'state': 'READY', 'description': 'South 24 Parganas_33_2017', 'priority': 100, 'creation_timestamp_ms': 1762658039477, 'update_timestamp_ms': 1762658039477, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'ZQIBCHQZ5GSCYOUFQI3A3UVG', 'name': 'projects/ext-datasets/operations/ZQIBCHQZ5GSCYOUFQI3A3UVG'}\n","Grid 34\n","curr_year 2017\n","Saving data for South 24 Parganas 2017\n","Task Started {'state': 'READY', 'description': 'South 24 Parganas_34_2017', 'priority': 100, 'creation_timestamp_ms': 1762658045365, 'update_timestamp_ms': 1762658045365, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '4XOT7VGN5VILILS63LLUZHCK', 'name': 'projects/ext-datasets/operations/4XOT7VGN5VILILS63LLUZHCK'}\n","Grid 35\n","curr_year 2017\n","Saving data for South 24 Parganas 2017\n","Task Started {'state': 'READY', 'description': 'South 24 Parganas_35_2017', 'priority': 100, 'creation_timestamp_ms': 1762658050426, 'update_timestamp_ms': 1762658050426, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'NTDMYCDK3IMXBZ2K7VKKN2EN', 'name': 'projects/ext-datasets/operations/NTDMYCDK3IMXBZ2K7VKKN2EN'}\n","Grid 36\n","curr_year 2017\n","Saving data for South 24 Parganas 2017\n","Task Started {'state': 'READY', 'description': 'South 24 Parganas_36_2017', 'priority': 100, 'creation_timestamp_ms': 1762658060828, 'update_timestamp_ms': 1762658060828, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'V5ONBRTZRMNIIE5UNB4B46RT', 'name': 'projects/ext-datasets/operations/V5ONBRTZRMNIIE5UNB4B46RT'}\n","Grid 37\n","curr_year 2017\n","Saving data for South 24 Parganas 2017\n","Task Started {'state': 'READY', 'description': 'South 24 Parganas_37_2017', 'priority': 100, 'creation_timestamp_ms': 1762658070675, 'update_timestamp_ms': 1762658070675, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'GF5TP6HSVDSEPOUTJJRKIK5X', 'name': 'projects/ext-datasets/operations/GF5TP6HSVDSEPOUTJJRKIK5X'}\n","Grid 38\n","curr_year 2017\n","Saving data for South 24 Parganas 2017\n","Task Started {'state': 'READY', 'description': 'South 24 Parganas_38_2017', 'priority': 100, 'creation_timestamp_ms': 1762658078959, 'update_timestamp_ms': 1762658078959, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'CJXDQHZN5ME55NN7ZP4SF35N', 'name': 'projects/ext-datasets/operations/CJXDQHZN5ME55NN7ZP4SF35N'}\n","Grid 39\n","curr_year 2017\n","Saving data for South 24 Parganas 2017\n","Task Started {'state': 'READY', 'description': 'South 24 Parganas_39_2017', 'priority': 100, 'creation_timestamp_ms': 1762658084150, 'update_timestamp_ms': 1762658084150, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'IOFWUZRXS2O4UVG4ZT5F5YYJ', 'name': 'projects/ext-datasets/operations/IOFWUZRXS2O4UVG4ZT5F5YYJ'}\n","Grid 40\n","curr_year 2017\n","Saving data for South 24 Parganas 2017\n","Task Started {'state': 'READY', 'description': 'South 24 Parganas_40_2017', 'priority': 100, 'creation_timestamp_ms': 1762658094491, 'update_timestamp_ms': 1762658094491, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'SGHSFIDOMYNXJKBWODVTUE3I', 'name': 'projects/ext-datasets/operations/SGHSFIDOMYNXJKBWODVTUE3I'}\n","Grid 41\n","curr_year 2017\n","Saving data for South 24 Parganas 2017\n","Task Started {'state': 'READY', 'description': 'South 24 Parganas_41_2017', 'priority': 100, 'creation_timestamp_ms': 1762658099479, 'update_timestamp_ms': 1762658099479, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'XZ6L6VGYP573JOYHI7AK6EVL', 'name': 'projects/ext-datasets/operations/XZ6L6VGYP573JOYHI7AK6EVL'}\n","Grid 42\n","curr_year 2017\n","Saving data for South 24 Parganas 2017\n","Task Started {'state': 'READY', 'description': 'South 24 Parganas_42_2017', 'priority': 100, 'creation_timestamp_ms': 1762658104123, 'update_timestamp_ms': 1762658104123, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '4T46VWLU42FSHPQNNN7G2KS3', 'name': 'projects/ext-datasets/operations/4T46VWLU42FSHPQNNN7G2KS3'}\n","Grid 43\n","curr_year 2017\n","Saving data for South 24 Parganas 2017\n","Task Started {'state': 'READY', 'description': 'South 24 Parganas_43_2017', 'priority': 100, 'creation_timestamp_ms': 1762658114192, 'update_timestamp_ms': 1762658114192, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'N34EYKWSDADLOYRZCZQ54UMY', 'name': 'projects/ext-datasets/operations/N34EYKWSDADLOYRZCZQ54UMY'}\n","Grid 44\n","curr_year 2017\n","Saving data for South 24 Parganas 2017\n","Task Started {'state': 'READY', 'description': 'South 24 Parganas_44_2017', 'priority': 100, 'creation_timestamp_ms': 1762658123158, 'update_timestamp_ms': 1762658123158, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '2BHSCBJJHNJROJBZP27657WY', 'name': 'projects/ext-datasets/operations/2BHSCBJJHNJROJBZP27657WY'}\n","Grid 45\n","curr_year 2017\n","Saving data for South 24 Parganas 2017\n","Task Started {'state': 'READY', 'description': 'South 24 Parganas_45_2017', 'priority': 100, 'creation_timestamp_ms': 1762658131902, 'update_timestamp_ms': 1762658131902, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '2B2HHU7LRQCTGWJFIZ2B6IZO', 'name': 'projects/ext-datasets/operations/2B2HHU7LRQCTGWJFIZ2B6IZO'}\n","Grid 46\n","curr_year 2017\n","Saving data for South 24 Parganas 2017\n","Task Started {'state': 'READY', 'description': 'South 24 Parganas_46_2017', 'priority': 100, 'creation_timestamp_ms': 1762658139038, 'update_timestamp_ms': 1762658139038, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'KSEBDSG3ESM6ZZH2YL5SRAFF', 'name': 'projects/ext-datasets/operations/KSEBDSG3ESM6ZZH2YL5SRAFF'}\n","Grid 47\n","curr_year 2017\n","Saving data for South 24 Parganas 2017\n","Task Started {'state': 'READY', 'description': 'South 24 Parganas_47_2017', 'priority': 100, 'creation_timestamp_ms': 1762658148373, 'update_timestamp_ms': 1762658148373, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'LWBZ7EZT3JLKW4PLGKBSURAE', 'name': 'projects/ext-datasets/operations/LWBZ7EZT3JLKW4PLGKBSURAE'}\n","Grid 48\n","curr_year 2017\n","Saving data for South 24 Parganas 2017\n","Task Started {'state': 'READY', 'description': 'South 24 Parganas_48_2017', 'priority': 100, 'creation_timestamp_ms': 1762658156748, 'update_timestamp_ms': 1762658156748, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '64YDSCGZFGFYXDYOVZCIVKPU', 'name': 'projects/ext-datasets/operations/64YDSCGZFGFYXDYOVZCIVKPU'}\n","Grid 49\n","curr_year 2017\n","Saving data for South 24 Parganas 2017\n","Task Started {'state': 'READY', 'description': 'South 24 Parganas_49_2017', 'priority': 100, 'creation_timestamp_ms': 1762658163124, 'update_timestamp_ms': 1762658163124, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'HCSBKBCRPOD66HQU62UXAIRW', 'name': 'projects/ext-datasets/operations/HCSBKBCRPOD66HQU62UXAIRW'}\n","Grid 50\n","curr_year 2017\n","Saving data for South 24 Parganas 2017\n","Task Started {'state': 'READY', 'description': 'South 24 Parganas_50_2017', 'priority': 100, 'creation_timestamp_ms': 1762658172736, 'update_timestamp_ms': 1762658172736, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '6CANJ4KVKVSHZVFXDHJLQEU6', 'name': 'projects/ext-datasets/operations/6CANJ4KVKVSHZVFXDHJLQEU6'}\n","Grid 51\n","curr_year 2017\n","Saving data for South 24 Parganas 2017\n","Task Started {'state': 'READY', 'description': 'South 24 Parganas_51_2017', 'priority': 100, 'creation_timestamp_ms': 1762658183522, 'update_timestamp_ms': 1762658183522, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'E2XDHC6U6ONKZ4SSNF2HL5YZ', 'name': 'projects/ext-datasets/operations/E2XDHC6U6ONKZ4SSNF2HL5YZ'}\n","Grid 52\n","curr_year 2017\n","Saving data for South 24 Parganas 2017\n","Task Started {'state': 'READY', 'description': 'South 24 Parganas_52_2017', 'priority': 100, 'creation_timestamp_ms': 1762658189528, 'update_timestamp_ms': 1762658189528, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'XS2IZR7Y5P26CONDEK2FNN4Y', 'name': 'projects/ext-datasets/operations/XS2IZR7Y5P26CONDEK2FNN4Y'}\n","Grid 53\n","curr_year 2017\n","Saving data for South 24 Parganas 2017\n","Task Started {'state': 'READY', 'description': 'South 24 Parganas_53_2017', 'priority': 100, 'creation_timestamp_ms': 1762658198178, 'update_timestamp_ms': 1762658198178, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'Q4R3ZNMSLY64M4M7YM7MPW5R', 'name': 'projects/ext-datasets/operations/Q4R3ZNMSLY64M4M7YM7MPW5R'}\n","Grid 54\n","curr_year 2017\n","Saving data for South 24 Parganas 2017\n","Task Started {'state': 'READY', 'description': 'South 24 Parganas_54_2017', 'priority': 100, 'creation_timestamp_ms': 1762658208255, 'update_timestamp_ms': 1762658208255, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'NB53FPAQI4R4LNJVGW5PEADO', 'name': 'projects/ext-datasets/operations/NB53FPAQI4R4LNJVGW5PEADO'}\n","Grid 55\n","curr_year 2017\n","Saving data for South 24 Parganas 2017\n","Task Started {'state': 'READY', 'description': 'South 24 Parganas_55_2017', 'priority': 100, 'creation_timestamp_ms': 1762658218559, 'update_timestamp_ms': 1762658218559, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'L2LCIJDEPBN5XK76UJB2KDKC', 'name': 'projects/ext-datasets/operations/L2LCIJDEPBN5XK76UJB2KDKC'}\n","Grid 56\n","curr_year 2017\n","Saving data for South 24 Parganas 2017\n","Task Started {'state': 'READY', 'description': 'South 24 Parganas_56_2017', 'priority': 100, 'creation_timestamp_ms': 1762658226990, 'update_timestamp_ms': 1762658226990, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '256PZ72MZY26EZB4LZQJIIY3', 'name': 'projects/ext-datasets/operations/256PZ72MZY26EZB4LZQJIIY3'}\n","Grid 57\n","curr_year 2017\n","Saving data for South 24 Parganas 2017\n","Task Started {'state': 'READY', 'description': 'South 24 Parganas_57_2017', 'priority': 100, 'creation_timestamp_ms': 1762658238553, 'update_timestamp_ms': 1762658238553, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'RPJZSCLQAV5FOUYTKI6XGIOP', 'name': 'projects/ext-datasets/operations/RPJZSCLQAV5FOUYTKI6XGIOP'}\n","Grid 58\n","curr_year 2017\n","Saving data for South 24 Parganas 2017\n","Task Started {'state': 'READY', 'description': 'South 24 Parganas_58_2017', 'priority': 100, 'creation_timestamp_ms': 1762658250452, 'update_timestamp_ms': 1762658250452, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'XYUVV2JI7FN5APGHMBZG5A2F', 'name': 'projects/ext-datasets/operations/XYUVV2JI7FN5APGHMBZG5A2F'}\n","Grid 59\n","curr_year 2017\n","Saving data for South 24 Parganas 2017\n","Task Started {'state': 'READY', 'description': 'South 24 Parganas_59_2017', 'priority': 100, 'creation_timestamp_ms': 1762658256418, 'update_timestamp_ms': 1762658256418, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'IOCEQRFICZYF7LG5DET6G433', 'name': 'projects/ext-datasets/operations/IOCEQRFICZYF7LG5DET6G433'}\n","Grid 60\n","curr_year 2017\n","Saving data for South 24 Parganas 2017\n","Task Started {'state': 'READY', 'description': 'South 24 Parganas_60_2017', 'priority': 100, 'creation_timestamp_ms': 1762658270323, 'update_timestamp_ms': 1762658270323, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'LZ4JFOGPOPC4NGO2Z4JUZM3R', 'name': 'projects/ext-datasets/operations/LZ4JFOGPOPC4NGO2Z4JUZM3R'}\n","Grid 61\n","curr_year 2017\n","Saving data for South 24 Parganas 2017\n","Task Started {'state': 'READY', 'description': 'South 24 Parganas_61_2017', 'priority': 100, 'creation_timestamp_ms': 1762658280593, 'update_timestamp_ms': 1762658280593, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'MQMOCAX5X2U4L4IBBKMOHAXA', 'name': 'projects/ext-datasets/operations/MQMOCAX5X2U4L4IBBKMOHAXA'}\n","Grid 62\n","curr_year 2017\n","Saving data for South 24 Parganas 2017\n","Task Started {'state': 'READY', 'description': 'South 24 Parganas_62_2017', 'priority': 100, 'creation_timestamp_ms': 1762658292209, 'update_timestamp_ms': 1762658292209, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'RJ5EVGBN54NAWMTPA7W5N77Z', 'name': 'projects/ext-datasets/operations/RJ5EVGBN54NAWMTPA7W5N77Z'}\n","Grid 63\n","curr_year 2017\n","Saving data for South 24 Parganas 2017\n","Task Started {'state': 'READY', 'description': 'South 24 Parganas_63_2017', 'priority': 100, 'creation_timestamp_ms': 1762658302123, 'update_timestamp_ms': 1762658302123, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'TGFFL47X7YC6OWXJZQO2XQYE', 'name': 'projects/ext-datasets/operations/TGFFL47X7YC6OWXJZQO2XQYE'}\n","Grid 64\n","curr_year 2017\n","Saving data for South 24 Parganas 2017\n","Task Started {'state': 'READY', 'description': 'South 24 Parganas_64_2017', 'priority': 100, 'creation_timestamp_ms': 1762658310424, 'update_timestamp_ms': 1762658310424, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '3RTHW772TDXSGOMGTPBPCIIU', 'name': 'projects/ext-datasets/operations/3RTHW772TDXSGOMGTPBPCIIU'}\n","Grid 65\n","curr_year 2017\n","Saving data for South 24 Parganas 2017\n","Task Started {'state': 'READY', 'description': 'South 24 Parganas_65_2017', 'priority': 100, 'creation_timestamp_ms': 1762658319876, 'update_timestamp_ms': 1762658319876, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'LQUWEEH2X7ZAQHBVKLPWRBJZ', 'name': 'projects/ext-datasets/operations/LQUWEEH2X7ZAQHBVKLPWRBJZ'}\n","Grid 66\n","curr_year 2017\n","Saving data for South 24 Parganas 2017\n","Task Started {'state': 'READY', 'description': 'South 24 Parganas_66_2017', 'priority': 100, 'creation_timestamp_ms': 1762658325522, 'update_timestamp_ms': 1762658325522, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'ALNBVM2EM24TUHHAIC6MIZLY', 'name': 'projects/ext-datasets/operations/ALNBVM2EM24TUHHAIC6MIZLY'}\n","Grid 67\n","curr_year 2017\n","Saving data for South 24 Parganas 2017\n","Task Started {'state': 'READY', 'description': 'South 24 Parganas_67_2017', 'priority': 100, 'creation_timestamp_ms': 1762658333125, 'update_timestamp_ms': 1762658333125, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'UJQJYFMAUBQTHMB3R6KHPBS7', 'name': 'projects/ext-datasets/operations/UJQJYFMAUBQTHMB3R6KHPBS7'}\n","Grid 68\n","curr_year 2017\n","Saving data for South 24 Parganas 2017\n","Task Started {'state': 'READY', 'description': 'South 24 Parganas_68_2017', 'priority': 100, 'creation_timestamp_ms': 1762658341309, 'update_timestamp_ms': 1762658341309, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '4KRD7OSP4CGHHPVIJAW55W6F', 'name': 'projects/ext-datasets/operations/4KRD7OSP4CGHHPVIJAW55W6F'}\n","Grid 69\n","curr_year 2017\n","Saving data for South 24 Parganas 2017\n","Task Started {'state': 'READY', 'description': 'South 24 Parganas_69_2017', 'priority': 100, 'creation_timestamp_ms': 1762658355706, 'update_timestamp_ms': 1762658355706, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'JEJYU2S7LQX42S26BTHTA2UL', 'name': 'projects/ext-datasets/operations/JEJYU2S7LQX42S26BTHTA2UL'}\n","Grid 70\n","curr_year 2017\n","Saving data for South 24 Parganas 2017\n","Task Started {'state': 'READY', 'description': 'South 24 Parganas_70_2017', 'priority': 100, 'creation_timestamp_ms': 1762658361264, 'update_timestamp_ms': 1762658361264, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'F5NAZNHD5E34DLUFOUXSIA4U', 'name': 'projects/ext-datasets/operations/F5NAZNHD5E34DLUFOUXSIA4U'}\n","Grid 71\n","curr_year 2017\n","Saving data for South 24 Parganas 2017\n","Task Started {'state': 'READY', 'description': 'South 24 Parganas_71_2017', 'priority': 100, 'creation_timestamp_ms': 1762658366655, 'update_timestamp_ms': 1762658366655, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'ULJKUUTZ2PG67WRCGZFFLDZK', 'name': 'projects/ext-datasets/operations/ULJKUUTZ2PG67WRCGZFFLDZK'}\n","Grid 72\n","curr_year 2017\n","Saving data for South 24 Parganas 2017\n","Task Started {'state': 'READY', 'description': 'South 24 Parganas_72_2017', 'priority': 100, 'creation_timestamp_ms': 1762658382194, 'update_timestamp_ms': 1762658382194, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'VORWVY26L24WUPPRW3FXLSDD', 'name': 'projects/ext-datasets/operations/VORWVY26L24WUPPRW3FXLSDD'}\n","Grid 73\n","curr_year 2017\n","Saving data for South 24 Parganas 2017\n","Task Started {'state': 'READY', 'description': 'South 24 Parganas_73_2017', 'priority': 100, 'creation_timestamp_ms': 1762658392229, 'update_timestamp_ms': 1762658392229, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '7FJPJMR5LMLUUJFYLDNASLOF', 'name': 'projects/ext-datasets/operations/7FJPJMR5LMLUUJFYLDNASLOF'}\n","Grid 74\n","curr_year 2017\n","Saving data for South 24 Parganas 2017\n","Task Started {'state': 'READY', 'description': 'South 24 Parganas_74_2017', 'priority': 100, 'creation_timestamp_ms': 1762658403918, 'update_timestamp_ms': 1762658403918, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'KEITH5KA5SUJXJAUYW3ECCQA', 'name': 'projects/ext-datasets/operations/KEITH5KA5SUJXJAUYW3ECCQA'}\n","Grid 75\n","curr_year 2017\n","Saving data for South 24 Parganas 2017\n","Task Started {'state': 'READY', 'description': 'South 24 Parganas_75_2017', 'priority': 100, 'creation_timestamp_ms': 1762658411665, 'update_timestamp_ms': 1762658411665, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '2VKDQRLVUCXGXKKKH6SVUZAA', 'name': 'projects/ext-datasets/operations/2VKDQRLVUCXGXKKKH6SVUZAA'}\n","Grid 76\n","curr_year 2017\n","Saving data for South 24 Parganas 2017\n","Task Started {'state': 'READY', 'description': 'South 24 Parganas_76_2017', 'priority': 100, 'creation_timestamp_ms': 1762658420266, 'update_timestamp_ms': 1762658420266, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'HKGODGCCMJIDHJLSBJCQSLLZ', 'name': 'projects/ext-datasets/operations/HKGODGCCMJIDHJLSBJCQSLLZ'}\n","Grid 77\n","curr_year 2017\n","Saving data for South 24 Parganas 2017\n","Task Started {'state': 'READY', 'description': 'South 24 Parganas_77_2017', 'priority': 100, 'creation_timestamp_ms': 1762658431524, 'update_timestamp_ms': 1762658431524, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'TNJ7E433ZSQRKG4LKN2IIVYM', 'name': 'projects/ext-datasets/operations/TNJ7E433ZSQRKG4LKN2IIVYM'}\n","Grid 78\n","curr_year 2017\n","Saving data for South 24 Parganas 2017\n","Task Started {'state': 'READY', 'description': 'South 24 Parganas_78_2017', 'priority': 100, 'creation_timestamp_ms': 1762658437803, 'update_timestamp_ms': 1762658437803, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '4QLZZOBRVOCEZJOYK7FXDAFH', 'name': 'projects/ext-datasets/operations/4QLZZOBRVOCEZJOYK7FXDAFH'}\n","Grid 79\n","curr_year 2017\n","Saving data for South 24 Parganas 2017\n","Task Started {'state': 'READY', 'description': 'South 24 Parganas_79_2017', 'priority': 100, 'creation_timestamp_ms': 1762658446019, 'update_timestamp_ms': 1762658446019, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'EP5FJKS4UFY7BRL5YYLOBTBY', 'name': 'projects/ext-datasets/operations/EP5FJKS4UFY7BRL5YYLOBTBY'}\n","Grid 80\n","curr_year 2017\n","Saving data for South 24 Parganas 2017\n","Task Started {'state': 'READY', 'description': 'South 24 Parganas_80_2017', 'priority': 100, 'creation_timestamp_ms': 1762658450410, 'update_timestamp_ms': 1762658450410, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'TC2UYMSQ33VR6Y3FVWQOSGVI', 'name': 'projects/ext-datasets/operations/TC2UYMSQ33VR6Y3FVWQOSGVI'}\n","Grid 81\n","curr_year 2017\n","Saving data for South 24 Parganas 2017\n","Task Started {'state': 'READY', 'description': 'South 24 Parganas_81_2017', 'priority': 100, 'creation_timestamp_ms': 1762658455489, 'update_timestamp_ms': 1762658455489, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '6MPIM32AWN5QJRZ3XFYVJ2MD', 'name': 'projects/ext-datasets/operations/6MPIM32AWN5QJRZ3XFYVJ2MD'}\n","Grid 82\n","curr_year 2017\n","Saving data for South 24 Parganas 2017\n","Task Started {'state': 'READY', 'description': 'South 24 Parganas_82_2017', 'priority': 100, 'creation_timestamp_ms': 1762658465865, 'update_timestamp_ms': 1762658465865, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '7N7K6WQUV5CCL637CV4JWXB5', 'name': 'projects/ext-datasets/operations/7N7K6WQUV5CCL637CV4JWXB5'}\n","Grid 83\n","curr_year 2017\n","Saving data for South 24 Parganas 2017\n","Task Started {'state': 'READY', 'description': 'South 24 Parganas_83_2017', 'priority': 100, 'creation_timestamp_ms': 1762658474279, 'update_timestamp_ms': 1762658474279, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'NPOFQ37SR5OCIZ5C3V6NHIH3', 'name': 'projects/ext-datasets/operations/NPOFQ37SR5OCIZ5C3V6NHIH3'}\n","Grid 84\n","curr_year 2017\n","Saving data for South 24 Parganas 2017\n","Task Started {'state': 'READY', 'description': 'South 24 Parganas_84_2017', 'priority': 100, 'creation_timestamp_ms': 1762658483657, 'update_timestamp_ms': 1762658483657, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'HHMJILEVDQPYR3FOR52XBP3Y', 'name': 'projects/ext-datasets/operations/HHMJILEVDQPYR3FOR52XBP3Y'}\n","Grid 85\n","curr_year 2017\n","Saving data for South 24 Parganas 2017\n","Task Started {'state': 'READY', 'description': 'South 24 Parganas_85_2017', 'priority': 100, 'creation_timestamp_ms': 1762658494915, 'update_timestamp_ms': 1762658494915, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'Y2Q3XCWM7A7PRAZ5KT4ZDAV6', 'name': 'projects/ext-datasets/operations/Y2Q3XCWM7A7PRAZ5KT4ZDAV6'}\n","Grid 86\n","curr_year 2017\n","Saving data for South 24 Parganas 2017\n","Task Started {'state': 'READY', 'description': 'South 24 Parganas_86_2017', 'priority': 100, 'creation_timestamp_ms': 1762658503819, 'update_timestamp_ms': 1762658503819, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'FPYYKEFO4KM3TXHPYYJ5I3GX', 'name': 'projects/ext-datasets/operations/FPYYKEFO4KM3TXHPYYJ5I3GX'}\n","Grid 87\n","curr_year 2017\n","Saving data for South 24 Parganas 2017\n","Task Started {'state': 'READY', 'description': 'South 24 Parganas_87_2017', 'priority': 100, 'creation_timestamp_ms': 1762658508782, 'update_timestamp_ms': 1762658508782, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'GEFDNCSDWP4JQDVDRNNMTJDA', 'name': 'projects/ext-datasets/operations/GEFDNCSDWP4JQDVDRNNMTJDA'}\n","Grid 88\n","curr_year 2017\n","Saving data for South 24 Parganas 2017\n","Task Started {'state': 'READY', 'description': 'South 24 Parganas_88_2017', 'priority': 100, 'creation_timestamp_ms': 1762658516796, 'update_timestamp_ms': 1762658516796, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'HGMZ4DUJBVWIFRR2ZFMZN2MN', 'name': 'projects/ext-datasets/operations/HGMZ4DUJBVWIFRR2ZFMZN2MN'}\n","Grid 89\n","curr_year 2017\n","Saving data for South 24 Parganas 2017\n","Task Started {'state': 'READY', 'description': 'South 24 Parganas_89_2017', 'priority': 100, 'creation_timestamp_ms': 1762658522030, 'update_timestamp_ms': 1762658522030, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'VO5ODDADPHD456GDIW3PKDPF', 'name': 'projects/ext-datasets/operations/VO5ODDADPHD456GDIW3PKDPF'}\n","Grid 90\n","curr_year 2017\n","Saving data for South 24 Parganas 2017\n","Task Started {'state': 'READY', 'description': 'South 24 Parganas_90_2017', 'priority': 100, 'creation_timestamp_ms': 1762658531380, 'update_timestamp_ms': 1762658531380, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'RI3OI4JF5LXQUBZZA2N3QSSA', 'name': 'projects/ext-datasets/operations/RI3OI4JF5LXQUBZZA2N3QSSA'}\n","Grid 91\n","curr_year 2017\n","Saving data for South 24 Parganas 2017\n","Task Started {'state': 'READY', 'description': 'South 24 Parganas_91_2017', 'priority': 100, 'creation_timestamp_ms': 1762658537526, 'update_timestamp_ms': 1762658537526, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'YHYVHZDHXUO575ETOIOGZBR3', 'name': 'projects/ext-datasets/operations/YHYVHZDHXUO575ETOIOGZBR3'}\n","Grid 92\n","curr_year 2017\n","Saving data for South 24 Parganas 2017\n","Task Started {'state': 'READY', 'description': 'South 24 Parganas_92_2017', 'priority': 100, 'creation_timestamp_ms': 1762658546056, 'update_timestamp_ms': 1762658546056, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'DLENUAUSWOUI6FUO3NJK5S4X', 'name': 'projects/ext-datasets/operations/DLENUAUSWOUI6FUO3NJK5S4X'}\n","Grid 93\n","curr_year 2017\n","Saving data for South 24 Parganas 2017\n","Task Started {'state': 'READY', 'description': 'South 24 Parganas_93_2017', 'priority': 100, 'creation_timestamp_ms': 1762658556367, 'update_timestamp_ms': 1762658556367, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'SKVBGIOHI3KOZG4AXLQJCA3V', 'name': 'projects/ext-datasets/operations/SKVBGIOHI3KOZG4AXLQJCA3V'}\n","Grid 94\n","curr_year 2017\n","Saving data for South 24 Parganas 2017\n","Task Started {'state': 'READY', 'description': 'South 24 Parganas_94_2017', 'priority': 100, 'creation_timestamp_ms': 1762658566574, 'update_timestamp_ms': 1762658566574, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'NBKVSQ6ACYIHYENK3RVO6PGH', 'name': 'projects/ext-datasets/operations/NBKVSQ6ACYIHYENK3RVO6PGH'}\n","Grid 95\n","curr_year 2017\n","Saving data for South 24 Parganas 2017\n","Task Started {'state': 'READY', 'description': 'South 24 Parganas_95_2017', 'priority': 100, 'creation_timestamp_ms': 1762658570912, 'update_timestamp_ms': 1762658570912, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '5UZ57KTGRFOTCHMAP2XZN6XK', 'name': 'projects/ext-datasets/operations/5UZ57KTGRFOTCHMAP2XZN6XK'}\n","9024.560793161392\n","Year 2017, District 27: Uttar Dinajpur, grids: 66\n","Grid 0\n","curr_year 2017\n","Saving data for Uttar Dinajpur 2017\n","Task Started {'state': 'READY', 'description': 'Uttar Dinajpur_0_2017', 'priority': 100, 'creation_timestamp_ms': 1762658581084, 'update_timestamp_ms': 1762658581084, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'LWBBTEE4OQIGGA2Y4G7QRL3B', 'name': 'projects/ext-datasets/operations/LWBBTEE4OQIGGA2Y4G7QRL3B'}\n","Grid 1\n","curr_year 2017\n","Saving data for Uttar Dinajpur 2017\n","Task Started {'state': 'READY', 'description': 'Uttar Dinajpur_1_2017', 'priority': 100, 'creation_timestamp_ms': 1762658588334, 'update_timestamp_ms': 1762658588334, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'BHSAMUE5CFJZXG7SAJW5PYEC', 'name': 'projects/ext-datasets/operations/BHSAMUE5CFJZXG7SAJW5PYEC'}\n","Grid 2\n","curr_year 2017\n","Saving data for Uttar Dinajpur 2017\n","Task Started {'state': 'READY', 'description': 'Uttar Dinajpur_2_2017', 'priority': 100, 'creation_timestamp_ms': 1762658594508, 'update_timestamp_ms': 1762658594508, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'HJSG7LDRDUZP7PB2D6NVHWKZ', 'name': 'projects/ext-datasets/operations/HJSG7LDRDUZP7PB2D6NVHWKZ'}\n","Grid 3\n","curr_year 2017\n","Saving data for Uttar Dinajpur 2017\n","Task Started {'state': 'READY', 'description': 'Uttar Dinajpur_3_2017', 'priority': 100, 'creation_timestamp_ms': 1762658601690, 'update_timestamp_ms': 1762658601690, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'DWN4F3MWMNC55ZAXAXJHRGQH', 'name': 'projects/ext-datasets/operations/DWN4F3MWMNC55ZAXAXJHRGQH'}\n","Grid 4\n","curr_year 2017\n","Saving data for Uttar Dinajpur 2017\n","Task Started {'state': 'READY', 'description': 'Uttar Dinajpur_4_2017', 'priority': 100, 'creation_timestamp_ms': 1762658608815, 'update_timestamp_ms': 1762658608815, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'GUB7CAGUPH4KVCETAVJWTDYO', 'name': 'projects/ext-datasets/operations/GUB7CAGUPH4KVCETAVJWTDYO'}\n","Grid 5\n","curr_year 2017\n","Saving data for Uttar Dinajpur 2017\n","Task Started {'state': 'READY', 'description': 'Uttar Dinajpur_5_2017', 'priority': 100, 'creation_timestamp_ms': 1762658615599, 'update_timestamp_ms': 1762658615599, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'SLB2C4FUXIRXA6URE7QN4BTX', 'name': 'projects/ext-datasets/operations/SLB2C4FUXIRXA6URE7QN4BTX'}\n","Grid 6\n","curr_year 2017\n","Saving data for Uttar Dinajpur 2017\n","Task Started {'state': 'READY', 'description': 'Uttar Dinajpur_6_2017', 'priority': 100, 'creation_timestamp_ms': 1762658623281, 'update_timestamp_ms': 1762658623281, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '7PD3J3HTW4S2CBVRTAC6FZU4', 'name': 'projects/ext-datasets/operations/7PD3J3HTW4S2CBVRTAC6FZU4'}\n","Grid 7\n","curr_year 2017\n","Saving data for Uttar Dinajpur 2017\n","Task Started {'state': 'READY', 'description': 'Uttar Dinajpur_7_2017', 'priority': 100, 'creation_timestamp_ms': 1762658630336, 'update_timestamp_ms': 1762658630336, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '64FLPTQ3NXI36KU5PZCN5PS4', 'name': 'projects/ext-datasets/operations/64FLPTQ3NXI36KU5PZCN5PS4'}\n","Grid 8\n","curr_year 2017\n","Saving data for Uttar Dinajpur 2017\n","Task Started {'state': 'READY', 'description': 'Uttar Dinajpur_8_2017', 'priority': 100, 'creation_timestamp_ms': 1762658633947, 'update_timestamp_ms': 1762658633947, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'C2EHZ5CD5I7HLQJQWH3LXVXV', 'name': 'projects/ext-datasets/operations/C2EHZ5CD5I7HLQJQWH3LXVXV'}\n","Grid 9\n","curr_year 2017\n","Saving data for Uttar Dinajpur 2017\n","Task Started {'state': 'READY', 'description': 'Uttar Dinajpur_9_2017', 'priority': 100, 'creation_timestamp_ms': 1762658642174, 'update_timestamp_ms': 1762658642174, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'K3HEMYXEBW3WHHPG42ITU2YJ', 'name': 'projects/ext-datasets/operations/K3HEMYXEBW3WHHPG42ITU2YJ'}\n","Grid 10\n","curr_year 2017\n","Saving data for Uttar Dinajpur 2017\n","Task Started {'state': 'READY', 'description': 'Uttar Dinajpur_10_2017', 'priority': 100, 'creation_timestamp_ms': 1762658650276, 'update_timestamp_ms': 1762658650276, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'MXRWUZPDO5OBXYJ7WYSSA5Y3', 'name': 'projects/ext-datasets/operations/MXRWUZPDO5OBXYJ7WYSSA5Y3'}\n","Grid 11\n","curr_year 2017\n","Saving data for Uttar Dinajpur 2017\n","Task Started {'state': 'READY', 'description': 'Uttar Dinajpur_11_2017', 'priority': 100, 'creation_timestamp_ms': 1762658657079, 'update_timestamp_ms': 1762658657079, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'G3TZ7XSDMMJ2GZRH5GST2NIK', 'name': 'projects/ext-datasets/operations/G3TZ7XSDMMJ2GZRH5GST2NIK'}\n","Grid 12\n","curr_year 2017\n","Saving data for Uttar Dinajpur 2017\n","Task Started {'state': 'READY', 'description': 'Uttar Dinajpur_12_2017', 'priority': 100, 'creation_timestamp_ms': 1762658663272, 'update_timestamp_ms': 1762658663272, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'BN6YANDTBN5D4X2MHJCJLCDA', 'name': 'projects/ext-datasets/operations/BN6YANDTBN5D4X2MHJCJLCDA'}\n","Grid 13\n","curr_year 2017\n","Saving data for Uttar Dinajpur 2017\n","Task Started {'state': 'READY', 'description': 'Uttar Dinajpur_13_2017', 'priority': 100, 'creation_timestamp_ms': 1762658669155, 'update_timestamp_ms': 1762658669155, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'VFIYOHFXLL4T5SAFBKZ7UVIE', 'name': 'projects/ext-datasets/operations/VFIYOHFXLL4T5SAFBKZ7UVIE'}\n","Grid 14\n","curr_year 2017\n","Saving data for Uttar Dinajpur 2017\n","Task Started {'state': 'READY', 'description': 'Uttar Dinajpur_14_2017', 'priority': 100, 'creation_timestamp_ms': 1762658676404, 'update_timestamp_ms': 1762658676404, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'KHQJVAUYSK5VIX3LBZXM2L5V', 'name': 'projects/ext-datasets/operations/KHQJVAUYSK5VIX3LBZXM2L5V'}\n","Grid 15\n","curr_year 2017\n","Saving data for Uttar Dinajpur 2017\n","Task Started {'state': 'READY', 'description': 'Uttar Dinajpur_15_2017', 'priority': 100, 'creation_timestamp_ms': 1762658684794, 'update_timestamp_ms': 1762658684794, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '37USBI46MJ7PZVUQARNBFLYH', 'name': 'projects/ext-datasets/operations/37USBI46MJ7PZVUQARNBFLYH'}\n","Grid 16\n","curr_year 2017\n","Saving data for Uttar Dinajpur 2017\n","Task Started {'state': 'READY', 'description': 'Uttar Dinajpur_16_2017', 'priority': 100, 'creation_timestamp_ms': 1762658691124, 'update_timestamp_ms': 1762658691124, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '27JZ2HGHE3UX2GM74QLKCOEF', 'name': 'projects/ext-datasets/operations/27JZ2HGHE3UX2GM74QLKCOEF'}\n","Grid 17\n","curr_year 2017\n","Saving data for Uttar Dinajpur 2017\n","Task Started {'state': 'READY', 'description': 'Uttar Dinajpur_17_2017', 'priority': 100, 'creation_timestamp_ms': 1762658697706, 'update_timestamp_ms': 1762658697706, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'JADLTSZXIRCL2EW6ZCWMLMNQ', 'name': 'projects/ext-datasets/operations/JADLTSZXIRCL2EW6ZCWMLMNQ'}\n","Grid 18\n","curr_year 2017\n","Saving data for Uttar Dinajpur 2017\n","Task Started {'state': 'READY', 'description': 'Uttar Dinajpur_18_2017', 'priority': 100, 'creation_timestamp_ms': 1762658703798, 'update_timestamp_ms': 1762658703798, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'N7LJ5V3OD3SJ6BLRH2UH4YJ7', 'name': 'projects/ext-datasets/operations/N7LJ5V3OD3SJ6BLRH2UH4YJ7'}\n","Grid 19\n","curr_year 2017\n","Saving data for Uttar Dinajpur 2017\n","Task Started {'state': 'READY', 'description': 'Uttar Dinajpur_19_2017', 'priority': 100, 'creation_timestamp_ms': 1762658709708, 'update_timestamp_ms': 1762658709708, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'NPAHS4K5NYZX3ELW74FKPBMW', 'name': 'projects/ext-datasets/operations/NPAHS4K5NYZX3ELW74FKPBMW'}\n","Grid 20\n","curr_year 2017\n","Saving data for Uttar Dinajpur 2017\n","Task Started {'state': 'READY', 'description': 'Uttar Dinajpur_20_2017', 'priority': 100, 'creation_timestamp_ms': 1762658716807, 'update_timestamp_ms': 1762658716807, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'WXDUGDJI5HVT27NP6X7KW2G7', 'name': 'projects/ext-datasets/operations/WXDUGDJI5HVT27NP6X7KW2G7'}\n","Grid 21\n","curr_year 2017\n","Saving data for Uttar Dinajpur 2017\n","Task Started {'state': 'READY', 'description': 'Uttar Dinajpur_21_2017', 'priority': 100, 'creation_timestamp_ms': 1762658723206, 'update_timestamp_ms': 1762658723206, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'VVS75FZPKQVCHGVWSDTFLPOK', 'name': 'projects/ext-datasets/operations/VVS75FZPKQVCHGVWSDTFLPOK'}\n","Grid 22\n","curr_year 2017\n","Saving data for Uttar Dinajpur 2017\n","Task Started {'state': 'READY', 'description': 'Uttar Dinajpur_22_2017', 'priority': 100, 'creation_timestamp_ms': 1762658730930, 'update_timestamp_ms': 1762658730930, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'GCKIQVZTHE76DTQVIC35YHHI', 'name': 'projects/ext-datasets/operations/GCKIQVZTHE76DTQVIC35YHHI'}\n","Grid 23\n","curr_year 2017\n","Saving data for Uttar Dinajpur 2017\n","Task Started {'state': 'READY', 'description': 'Uttar Dinajpur_23_2017', 'priority': 100, 'creation_timestamp_ms': 1762658739413, 'update_timestamp_ms': 1762658739413, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'RQZFSXT2DG7GTPGPUDPDC5UU', 'name': 'projects/ext-datasets/operations/RQZFSXT2DG7GTPGPUDPDC5UU'}\n","Grid 24\n","curr_year 2017\n","Saving data for Uttar Dinajpur 2017\n","Task Started {'state': 'READY', 'description': 'Uttar Dinajpur_24_2017', 'priority': 100, 'creation_timestamp_ms': 1762658746912, 'update_timestamp_ms': 1762658746912, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '4NKGQ373GYKR2GMNPF2GLAJJ', 'name': 'projects/ext-datasets/operations/4NKGQ373GYKR2GMNPF2GLAJJ'}\n","Grid 25\n","curr_year 2017\n","Saving data for Uttar Dinajpur 2017\n","Task Started {'state': 'READY', 'description': 'Uttar Dinajpur_25_2017', 'priority': 100, 'creation_timestamp_ms': 1762658750696, 'update_timestamp_ms': 1762658750696, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'JHI6WM74MZ42YEQ733U462QE', 'name': 'projects/ext-datasets/operations/JHI6WM74MZ42YEQ733U462QE'}\n","Grid 26\n","curr_year 2017\n","Saving data for Uttar Dinajpur 2017\n","Task Started {'state': 'READY', 'description': 'Uttar Dinajpur_26_2017', 'priority': 100, 'creation_timestamp_ms': 1762658758452, 'update_timestamp_ms': 1762658758452, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'UUGIT6D53J6YJ7XE2LDEPGTE', 'name': 'projects/ext-datasets/operations/UUGIT6D53J6YJ7XE2LDEPGTE'}\n","Grid 27\n","curr_year 2017\n","Saving data for Uttar Dinajpur 2017\n","Task Started {'state': 'READY', 'description': 'Uttar Dinajpur_27_2017', 'priority': 100, 'creation_timestamp_ms': 1762658766194, 'update_timestamp_ms': 1762658766194, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'GDHLVVLG5UMB6LZEQZDF2FNU', 'name': 'projects/ext-datasets/operations/GDHLVVLG5UMB6LZEQZDF2FNU'}\n","Grid 28\n","curr_year 2017\n","Saving data for Uttar Dinajpur 2017\n","Task Started {'state': 'READY', 'description': 'Uttar Dinajpur_28_2017', 'priority': 100, 'creation_timestamp_ms': 1762658774377, 'update_timestamp_ms': 1762658774377, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'GWI2DLEAB62E6LCEW5LBHAO5', 'name': 'projects/ext-datasets/operations/GWI2DLEAB62E6LCEW5LBHAO5'}\n","Grid 29\n","curr_year 2017\n","Saving data for Uttar Dinajpur 2017\n","Task Started {'state': 'READY', 'description': 'Uttar Dinajpur_29_2017', 'priority': 100, 'creation_timestamp_ms': 1762658782303, 'update_timestamp_ms': 1762658782303, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'CBYE4GRNC6K7QCDMSAPYHT5S', 'name': 'projects/ext-datasets/operations/CBYE4GRNC6K7QCDMSAPYHT5S'}\n","Grid 30\n","curr_year 2017\n","Saving data for Uttar Dinajpur 2017\n","Task Started {'state': 'READY', 'description': 'Uttar Dinajpur_30_2017', 'priority': 100, 'creation_timestamp_ms': 1762658785716, 'update_timestamp_ms': 1762658785716, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '4NLNMCX7B7Z5VVFCXGAIWR33', 'name': 'projects/ext-datasets/operations/4NLNMCX7B7Z5VVFCXGAIWR33'}\n","Grid 31\n","curr_year 2017\n","Saving data for Uttar Dinajpur 2017\n","Task Started {'state': 'READY', 'description': 'Uttar Dinajpur_31_2017', 'priority': 100, 'creation_timestamp_ms': 1762658789331, 'update_timestamp_ms': 1762658789331, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'WMUMCQIC5YE4QL3BVUO3IFQK', 'name': 'projects/ext-datasets/operations/WMUMCQIC5YE4QL3BVUO3IFQK'}\n","Grid 32\n","curr_year 2017\n","Saving data for Uttar Dinajpur 2017\n","Task Started {'state': 'READY', 'description': 'Uttar Dinajpur_32_2017', 'priority': 100, 'creation_timestamp_ms': 1762658796969, 'update_timestamp_ms': 1762658796969, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'IYPPIKH6YWZC35QORRFLVMQ3', 'name': 'projects/ext-datasets/operations/IYPPIKH6YWZC35QORRFLVMQ3'}\n","Grid 33\n","curr_year 2017\n","Saving data for Uttar Dinajpur 2017\n","Task Started {'state': 'READY', 'description': 'Uttar Dinajpur_33_2017', 'priority': 100, 'creation_timestamp_ms': 1762658803366, 'update_timestamp_ms': 1762658803366, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'JBCZMUD2BSIALH3AFWH5ZLKC', 'name': 'projects/ext-datasets/operations/JBCZMUD2BSIALH3AFWH5ZLKC'}\n","Grid 34\n","curr_year 2017\n","Saving data for Uttar Dinajpur 2017\n","Task Started {'state': 'READY', 'description': 'Uttar Dinajpur_34_2017', 'priority': 100, 'creation_timestamp_ms': 1762658809327, 'update_timestamp_ms': 1762658809327, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'JT3RHD7AG6MVTDFV5V3WOXL7', 'name': 'projects/ext-datasets/operations/JT3RHD7AG6MVTDFV5V3WOXL7'}\n","Grid 35\n","curr_year 2017\n","Saving data for Uttar Dinajpur 2017\n","Task Started {'state': 'READY', 'description': 'Uttar Dinajpur_35_2017', 'priority': 100, 'creation_timestamp_ms': 1762658813413, 'update_timestamp_ms': 1762658813413, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '7UOFD5XSRLBJS632WJQ4CZBA', 'name': 'projects/ext-datasets/operations/7UOFD5XSRLBJS632WJQ4CZBA'}\n","Grid 36\n","curr_year 2017\n","Saving data for Uttar Dinajpur 2017\n","Task Started {'state': 'READY', 'description': 'Uttar Dinajpur_36_2017', 'priority': 100, 'creation_timestamp_ms': 1762658821265, 'update_timestamp_ms': 1762658821265, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'GVZXFZ6IJZGHRGPPXMGBSNUE', 'name': 'projects/ext-datasets/operations/GVZXFZ6IJZGHRGPPXMGBSNUE'}\n","Grid 37\n","curr_year 2017\n","Saving data for Uttar Dinajpur 2017\n","Task Started {'state': 'READY', 'description': 'Uttar Dinajpur_37_2017', 'priority': 100, 'creation_timestamp_ms': 1762658828449, 'update_timestamp_ms': 1762658828449, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'MSAJDQF7DT75USRI3SOVBJJC', 'name': 'projects/ext-datasets/operations/MSAJDQF7DT75USRI3SOVBJJC'}\n","Grid 38\n","curr_year 2017\n","Saving data for Uttar Dinajpur 2017\n","Task Started {'state': 'READY', 'description': 'Uttar Dinajpur_38_2017', 'priority': 100, 'creation_timestamp_ms': 1762658837439, 'update_timestamp_ms': 1762658837439, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'JFEWEUKOQQD625JX3LLCJOND', 'name': 'projects/ext-datasets/operations/JFEWEUKOQQD625JX3LLCJOND'}\n","Grid 39\n","curr_year 2017\n","Saving data for Uttar Dinajpur 2017\n","Task Started {'state': 'READY', 'description': 'Uttar Dinajpur_39_2017', 'priority': 100, 'creation_timestamp_ms': 1762658844483, 'update_timestamp_ms': 1762658844483, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'OKNWNZH6N7WXC6JZOJ3IYXZQ', 'name': 'projects/ext-datasets/operations/OKNWNZH6N7WXC6JZOJ3IYXZQ'}\n","Grid 40\n","curr_year 2017\n","Saving data for Uttar Dinajpur 2017\n","Task Started {'state': 'READY', 'description': 'Uttar Dinajpur_40_2017', 'priority': 100, 'creation_timestamp_ms': 1762658850942, 'update_timestamp_ms': 1762658850942, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'AGQGE46OLJA2Q57YAUBTBOTO', 'name': 'projects/ext-datasets/operations/AGQGE46OLJA2Q57YAUBTBOTO'}\n","Grid 41\n","curr_year 2017\n","Saving data for Uttar Dinajpur 2017\n","Task Started {'state': 'READY', 'description': 'Uttar Dinajpur_41_2017', 'priority': 100, 'creation_timestamp_ms': 1762658855118, 'update_timestamp_ms': 1762658855118, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '7TALTC5S5CMHMX5QSRE33QOY', 'name': 'projects/ext-datasets/operations/7TALTC5S5CMHMX5QSRE33QOY'}\n","Grid 42\n","curr_year 2017\n","Saving data for Uttar Dinajpur 2017\n","Task Started {'state': 'READY', 'description': 'Uttar Dinajpur_42_2017', 'priority': 100, 'creation_timestamp_ms': 1762658861803, 'update_timestamp_ms': 1762658861803, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'FNIC44O2HXFHWDANPRNECDFZ', 'name': 'projects/ext-datasets/operations/FNIC44O2HXFHWDANPRNECDFZ'}\n","Grid 43\n","curr_year 2017\n","Saving data for Uttar Dinajpur 2017\n","Task Started {'state': 'READY', 'description': 'Uttar Dinajpur_43_2017', 'priority': 100, 'creation_timestamp_ms': 1762658865791, 'update_timestamp_ms': 1762658865791, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '7APNDRV5FSXSL5RFPHI4XWU3', 'name': 'projects/ext-datasets/operations/7APNDRV5FSXSL5RFPHI4XWU3'}\n","Grid 44\n","curr_year 2017\n","Saving data for Uttar Dinajpur 2017\n","Task Started {'state': 'READY', 'description': 'Uttar Dinajpur_44_2017', 'priority': 100, 'creation_timestamp_ms': 1762658872691, 'update_timestamp_ms': 1762658872691, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'I5VL7VQMHOJJH22N3WBRKBVF', 'name': 'projects/ext-datasets/operations/I5VL7VQMHOJJH22N3WBRKBVF'}\n","Grid 45\n","curr_year 2017\n","Saving data for Uttar Dinajpur 2017\n","Task Started {'state': 'READY', 'description': 'Uttar Dinajpur_45_2017', 'priority': 100, 'creation_timestamp_ms': 1762658879662, 'update_timestamp_ms': 1762658879662, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'GXAMQXEATIK6LMLCEQZTNFED', 'name': 'projects/ext-datasets/operations/GXAMQXEATIK6LMLCEQZTNFED'}\n","Grid 46\n","curr_year 2017\n","Saving data for Uttar Dinajpur 2017\n","Task Started {'state': 'READY', 'description': 'Uttar Dinajpur_46_2017', 'priority': 100, 'creation_timestamp_ms': 1762658888622, 'update_timestamp_ms': 1762658888622, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'JV2Z6W5AYFCKYAFMUI4MBUD7', 'name': 'projects/ext-datasets/operations/JV2Z6W5AYFCKYAFMUI4MBUD7'}\n","Grid 47\n","curr_year 2017\n","Saving data for Uttar Dinajpur 2017\n","Task Started {'state': 'READY', 'description': 'Uttar Dinajpur_47_2017', 'priority': 100, 'creation_timestamp_ms': 1762658892549, 'update_timestamp_ms': 1762658892549, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '2BF62FDMDSTNPOG3X7VVX2LD', 'name': 'projects/ext-datasets/operations/2BF62FDMDSTNPOG3X7VVX2LD'}\n","Grid 48\n","curr_year 2017\n","Saving data for Uttar Dinajpur 2017\n","Task Started {'state': 'READY', 'description': 'Uttar Dinajpur_48_2017', 'priority': 100, 'creation_timestamp_ms': 1762658900175, 'update_timestamp_ms': 1762658900175, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'WNBRTHWUPCGZNG6FGEBD6CRE', 'name': 'projects/ext-datasets/operations/WNBRTHWUPCGZNG6FGEBD6CRE'}\n","Grid 49\n","curr_year 2017\n","Saving data for Uttar Dinajpur 2017\n","Task Started {'state': 'READY', 'description': 'Uttar Dinajpur_49_2017', 'priority': 100, 'creation_timestamp_ms': 1762658907619, 'update_timestamp_ms': 1762658907619, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'DN2S6XIWJLNH5BR7XTXH5347', 'name': 'projects/ext-datasets/operations/DN2S6XIWJLNH5BR7XTXH5347'}\n","Grid 50\n","curr_year 2017\n","Saving data for Uttar Dinajpur 2017\n","Task Started {'state': 'READY', 'description': 'Uttar Dinajpur_50_2017', 'priority': 100, 'creation_timestamp_ms': 1762658917967, 'update_timestamp_ms': 1762658917967, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'HUUEVATPM33A4GCBAXMVGDY5', 'name': 'projects/ext-datasets/operations/HUUEVATPM33A4GCBAXMVGDY5'}\n","Grid 51\n","curr_year 2017\n","Saving data for Uttar Dinajpur 2017\n","Task Started {'state': 'READY', 'description': 'Uttar Dinajpur_51_2017', 'priority': 100, 'creation_timestamp_ms': 1762658927085, 'update_timestamp_ms': 1762658927085, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'F7K2YZTMLLWN36CJFTEYXSTQ', 'name': 'projects/ext-datasets/operations/F7K2YZTMLLWN36CJFTEYXSTQ'}\n","Grid 52\n","curr_year 2017\n","Saving data for Uttar Dinajpur 2017\n","Task Started {'state': 'READY', 'description': 'Uttar Dinajpur_52_2017', 'priority': 100, 'creation_timestamp_ms': 1762658933729, 'update_timestamp_ms': 1762658933729, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'RQL6KODKDQADPIVIYZOE6PSW', 'name': 'projects/ext-datasets/operations/RQL6KODKDQADPIVIYZOE6PSW'}\n","Grid 53\n","curr_year 2017\n","Saving data for Uttar Dinajpur 2017\n","Task Started {'state': 'READY', 'description': 'Uttar Dinajpur_53_2017', 'priority': 100, 'creation_timestamp_ms': 1762658940194, 'update_timestamp_ms': 1762658940194, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'K44YFWNUH4QCSZL3SDBFRCZL', 'name': 'projects/ext-datasets/operations/K44YFWNUH4QCSZL3SDBFRCZL'}\n","Grid 54\n","curr_year 2017\n","Saving data for Uttar Dinajpur 2017\n","Task Started {'state': 'READY', 'description': 'Uttar Dinajpur_54_2017', 'priority': 100, 'creation_timestamp_ms': 1762658947968, 'update_timestamp_ms': 1762658947968, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'T5NSIUR2WZQ3U4V2RCNMGSP4', 'name': 'projects/ext-datasets/operations/T5NSIUR2WZQ3U4V2RCNMGSP4'}\n","Grid 55\n","curr_year 2017\n","Saving data for Uttar Dinajpur 2017\n","Task Started {'state': 'READY', 'description': 'Uttar Dinajpur_55_2017', 'priority': 100, 'creation_timestamp_ms': 1762658953425, 'update_timestamp_ms': 1762658953425, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'SZ6MS7SNXMTXJZW746CFWWGV', 'name': 'projects/ext-datasets/operations/SZ6MS7SNXMTXJZW746CFWWGV'}\n","Grid 56\n","curr_year 2017\n","Saving data for Uttar Dinajpur 2017\n","Task Started {'state': 'READY', 'description': 'Uttar Dinajpur_56_2017', 'priority': 100, 'creation_timestamp_ms': 1762658961614, 'update_timestamp_ms': 1762658961614, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'AJTUSASAQMI7U4YPJJYT4MZO', 'name': 'projects/ext-datasets/operations/AJTUSASAQMI7U4YPJJYT4MZO'}\n","Grid 57\n","curr_year 2017\n","Saving data for Uttar Dinajpur 2017\n","Task Started {'state': 'READY', 'description': 'Uttar Dinajpur_57_2017', 'priority': 100, 'creation_timestamp_ms': 1762658967654, 'update_timestamp_ms': 1762658967654, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'BILBZACEENGO3RQ6KYFOWFIA', 'name': 'projects/ext-datasets/operations/BILBZACEENGO3RQ6KYFOWFIA'}\n","Grid 58\n","curr_year 2017\n","Saving data for Uttar Dinajpur 2017\n","Task Started {'state': 'READY', 'description': 'Uttar Dinajpur_58_2017', 'priority': 100, 'creation_timestamp_ms': 1762658975388, 'update_timestamp_ms': 1762658975388, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '37DNBONKMZIWZBDFL5BB5SKB', 'name': 'projects/ext-datasets/operations/37DNBONKMZIWZBDFL5BB5SKB'}\n","Grid 59\n","curr_year 2017\n","Saving data for Uttar Dinajpur 2017\n","Task Started {'state': 'READY', 'description': 'Uttar Dinajpur_59_2017', 'priority': 100, 'creation_timestamp_ms': 1762658982841, 'update_timestamp_ms': 1762658982841, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'QCBCXIOSOXSCIJD65Z5RGS37', 'name': 'projects/ext-datasets/operations/QCBCXIOSOXSCIJD65Z5RGS37'}\n","Grid 60\n","curr_year 2017\n","Saving data for Uttar Dinajpur 2017\n","Task Started {'state': 'READY', 'description': 'Uttar Dinajpur_60_2017', 'priority': 100, 'creation_timestamp_ms': 1762658990571, 'update_timestamp_ms': 1762658990571, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'FB73R4TB2R3G4GJF6LSHX3ZO', 'name': 'projects/ext-datasets/operations/FB73R4TB2R3G4GJF6LSHX3ZO'}\n","Grid 61\n","curr_year 2017\n","Saving data for Uttar Dinajpur 2017\n","Task Started {'state': 'READY', 'description': 'Uttar Dinajpur_61_2017', 'priority': 100, 'creation_timestamp_ms': 1762658998607, 'update_timestamp_ms': 1762658998607, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'ZIRZZXEGFVINO7RXYB2LTBM2', 'name': 'projects/ext-datasets/operations/ZIRZZXEGFVINO7RXYB2LTBM2'}\n","Grid 62\n","curr_year 2017\n","Saving data for Uttar Dinajpur 2017\n","Task Started {'state': 'READY', 'description': 'Uttar Dinajpur_62_2017', 'priority': 100, 'creation_timestamp_ms': 1762659006326, 'update_timestamp_ms': 1762659006326, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '64BFXGRHWSYYKXTST73LWYTW', 'name': 'projects/ext-datasets/operations/64BFXGRHWSYYKXTST73LWYTW'}\n","Grid 63\n","curr_year 2017\n","Saving data for Uttar Dinajpur 2017\n","Task Started {'state': 'READY', 'description': 'Uttar Dinajpur_63_2017', 'priority': 100, 'creation_timestamp_ms': 1762659013764, 'update_timestamp_ms': 1762659013764, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'HH6Y7YEXLISJDIJNZFKMPOEO', 'name': 'projects/ext-datasets/operations/HH6Y7YEXLISJDIJNZFKMPOEO'}\n","Grid 64\n","curr_year 2017\n","Saving data for Uttar Dinajpur 2017\n","Task Started {'state': 'READY', 'description': 'Uttar Dinajpur_64_2017', 'priority': 100, 'creation_timestamp_ms': 1762659020719, 'update_timestamp_ms': 1762659020719, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'XQQ6EU4UYTNRJODBMWAAFA4P', 'name': 'projects/ext-datasets/operations/XQQ6EU4UYTNRJODBMWAAFA4P'}\n","Grid 65\n","curr_year 2017\n","Saving data for Uttar Dinajpur 2017\n","Task Started {'state': 'READY', 'description': 'Uttar Dinajpur_65_2017', 'priority': 100, 'creation_timestamp_ms': 1762659024344, 'update_timestamp_ms': 1762659024344, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '6TVDYRTPWWZMLGBIE72ZVFYF', 'name': 'projects/ext-datasets/operations/6TVDYRTPWWZMLGBIE72ZVFYF'}\n","9478.025090694427\n","Waiting for last task to be completed...\n","Last task completed!\n","Total Time Taken: 11691.838443040848\n","Year 2018, District 0: Katihar, grids: 19\n","Grid 0\n","curr_year 2018\n","Saving data for Katihar 2018\n","Task Started {'state': 'READY', 'description': 'Katihar_0_2018', 'priority': 100, 'creation_timestamp_ms': 1762661252729, 'update_timestamp_ms': 1762661252729, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'QZYGCE6PYALXOSC6G5TLKOI6', 'name': 'projects/ext-datasets/operations/QZYGCE6PYALXOSC6G5TLKOI6'}\n","Grid 1\n","curr_year 2018\n","Saving data for Katihar 2018\n","Task Started {'state': 'READY', 'description': 'Katihar_1_2018', 'priority': 100, 'creation_timestamp_ms': 1762661259693, 'update_timestamp_ms': 1762661259693, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'L3CIIC4YVERUFHLZNGFQEM4U', 'name': 'projects/ext-datasets/operations/L3CIIC4YVERUFHLZNGFQEM4U'}\n","Grid 2\n","curr_year 2018\n","Saving data for Katihar 2018\n","Task Started {'state': 'READY', 'description': 'Katihar_2_2018', 'priority': 100, 'creation_timestamp_ms': 1762661267234, 'update_timestamp_ms': 1762661267234, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'GOOX3NPYWEG6DDKPVNUKDJMB', 'name': 'projects/ext-datasets/operations/GOOX3NPYWEG6DDKPVNUKDJMB'}\n","Grid 3\n","curr_year 2018\n","Saving data for Katihar 2018\n","Task Started {'state': 'READY', 'description': 'Katihar_3_2018', 'priority': 100, 'creation_timestamp_ms': 1762661274822, 'update_timestamp_ms': 1762661274822, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '7ENBZUSAEQTSFS5C5YYK3KD3', 'name': 'projects/ext-datasets/operations/7ENBZUSAEQTSFS5C5YYK3KD3'}\n","Grid 4\n","curr_year 2018\n","Saving data for Katihar 2018\n","Task Started {'state': 'READY', 'description': 'Katihar_4_2018', 'priority': 100, 'creation_timestamp_ms': 1762661280196, 'update_timestamp_ms': 1762661280196, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '7TAH2FLF5OCUW3YH4NNB2DHK', 'name': 'projects/ext-datasets/operations/7TAH2FLF5OCUW3YH4NNB2DHK'}\n","Grid 5\n","curr_year 2018\n","Saving data for Katihar 2018\n","Task Started {'state': 'READY', 'description': 'Katihar_5_2018', 'priority': 100, 'creation_timestamp_ms': 1762661287279, 'update_timestamp_ms': 1762661287279, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '2BAQGYWIEAEMIGF6FN5AHHYG', 'name': 'projects/ext-datasets/operations/2BAQGYWIEAEMIGF6FN5AHHYG'}\n","Grid 6\n","curr_year 2018\n","Saving data for Katihar 2018\n","Task Started {'state': 'READY', 'description': 'Katihar_6_2018', 'priority': 100, 'creation_timestamp_ms': 1762661296008, 'update_timestamp_ms': 1762661296008, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'FTW23QJPOIKIX2VXB2GNIEKH', 'name': 'projects/ext-datasets/operations/FTW23QJPOIKIX2VXB2GNIEKH'}\n","Grid 7\n","curr_year 2018\n","Saving data for Katihar 2018\n","Task Started {'state': 'READY', 'description': 'Katihar_7_2018', 'priority': 100, 'creation_timestamp_ms': 1762661305453, 'update_timestamp_ms': 1762661305453, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'CD4HQSTQX6J3TRY4YPIPGVKB', 'name': 'projects/ext-datasets/operations/CD4HQSTQX6J3TRY4YPIPGVKB'}\n","Grid 8\n","curr_year 2018\n","Saving data for Katihar 2018\n","Task Started {'state': 'READY', 'description': 'Katihar_8_2018', 'priority': 100, 'creation_timestamp_ms': 1762661314221, 'update_timestamp_ms': 1762661314221, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'H4PRAJ3M6CCRVVSEMVIXFH7W', 'name': 'projects/ext-datasets/operations/H4PRAJ3M6CCRVVSEMVIXFH7W'}\n","Grid 9\n","curr_year 2018\n","Saving data for Katihar 2018\n","Task Started {'state': 'READY', 'description': 'Katihar_9_2018', 'priority': 100, 'creation_timestamp_ms': 1762661321253, 'update_timestamp_ms': 1762661321253, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'HJFXG5X4NLN4L6LWRJTM3VJO', 'name': 'projects/ext-datasets/operations/HJFXG5X4NLN4L6LWRJTM3VJO'}\n","Grid 10\n","curr_year 2018\n","Saving data for Katihar 2018\n","Task Started {'state': 'READY', 'description': 'Katihar_10_2018', 'priority': 100, 'creation_timestamp_ms': 1762661332720, 'update_timestamp_ms': 1762661332720, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'NU63UW3MPLA4JJGQQRKOSE6M', 'name': 'projects/ext-datasets/operations/NU63UW3MPLA4JJGQQRKOSE6M'}\n","Grid 11\n","curr_year 2018\n","Saving data for Katihar 2018\n","Task Started {'state': 'READY', 'description': 'Katihar_11_2018', 'priority': 100, 'creation_timestamp_ms': 1762661341394, 'update_timestamp_ms': 1762661341394, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'WY3BYQEUH54SKBD45DIS34CA', 'name': 'projects/ext-datasets/operations/WY3BYQEUH54SKBD45DIS34CA'}\n","Grid 12\n","curr_year 2018\n","Saving data for Katihar 2018\n","Task Started {'state': 'READY', 'description': 'Katihar_12_2018', 'priority': 100, 'creation_timestamp_ms': 1762661352695, 'update_timestamp_ms': 1762661352695, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '7OJ5DW76UPJK4UDHOHAWSVC6', 'name': 'projects/ext-datasets/operations/7OJ5DW76UPJK4UDHOHAWSVC6'}\n","Grid 13\n","curr_year 2018\n","Saving data for Katihar 2018\n","Task Started {'state': 'READY', 'description': 'Katihar_13_2018', 'priority': 100, 'creation_timestamp_ms': 1762661359445, 'update_timestamp_ms': 1762661359445, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '2WSTPBBVG46QES57363AHD7H', 'name': 'projects/ext-datasets/operations/2WSTPBBVG46QES57363AHD7H'}\n","Grid 14\n","curr_year 2018\n","Saving data for Katihar 2018\n","Task Started {'state': 'READY', 'description': 'Katihar_14_2018', 'priority': 100, 'creation_timestamp_ms': 1762661366850, 'update_timestamp_ms': 1762661366850, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'CAGRXMPLNFMCRNIYR3NL4LQ6', 'name': 'projects/ext-datasets/operations/CAGRXMPLNFMCRNIYR3NL4LQ6'}\n","Grid 15\n","curr_year 2018\n","Saving data for Katihar 2018\n","Task Started {'state': 'READY', 'description': 'Katihar_15_2018', 'priority': 100, 'creation_timestamp_ms': 1762661372170, 'update_timestamp_ms': 1762661372170, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '2ZGWCRGI3FMR4KUP7FNKGKYU', 'name': 'projects/ext-datasets/operations/2ZGWCRGI3FMR4KUP7FNKGKYU'}\n","Grid 16\n","curr_year 2018\n","Saving data for Katihar 2018\n","Task Started {'state': 'READY', 'description': 'Katihar_16_2018', 'priority': 100, 'creation_timestamp_ms': 1762661382418, 'update_timestamp_ms': 1762661382418, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'RHSDM6ITGIDAIEJLVYOKERGB', 'name': 'projects/ext-datasets/operations/RHSDM6ITGIDAIEJLVYOKERGB'}\n","Grid 17\n","curr_year 2018\n","Saving data for Katihar 2018\n","Task Started {'state': 'READY', 'description': 'Katihar_17_2018', 'priority': 100, 'creation_timestamp_ms': 1762661390989, 'update_timestamp_ms': 1762661390989, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'M2Y3DIHVC2HQBLRT4CV5N34Y', 'name': 'projects/ext-datasets/operations/M2Y3DIHVC2HQBLRT4CV5N34Y'}\n","Grid 18\n","curr_year 2018\n","Saving data for Katihar 2018\n","Task Started {'state': 'READY', 'description': 'Katihar_18_2018', 'priority': 100, 'creation_timestamp_ms': 1762661401527, 'update_timestamp_ms': 1762661401527, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'EWPEIRAXHB7QVSU36K4VV4TF', 'name': 'projects/ext-datasets/operations/EWPEIRAXHB7QVSU36K4VV4TF'}\n","163.33016920089722\n","Year 2018, District 1: Kishanganj, grids: 10\n","Grid 0\n","curr_year 2018\n","Saving data for Kishanganj 2018\n","Task Started {'state': 'READY', 'description': 'Kishanganj_0_2018', 'priority': 100, 'creation_timestamp_ms': 1762661415169, 'update_timestamp_ms': 1762661415169, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'TYPM26KHDKOTX5PMCOW4H5ZB', 'name': 'projects/ext-datasets/operations/TYPM26KHDKOTX5PMCOW4H5ZB'}\n","Grid 1\n","curr_year 2018\n","Saving data for Kishanganj 2018\n","Task Started {'state': 'READY', 'description': 'Kishanganj_1_2018', 'priority': 100, 'creation_timestamp_ms': 1762661421109, 'update_timestamp_ms': 1762661421109, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'J7TRPDWY6BSAWHBDSV24FN6E', 'name': 'projects/ext-datasets/operations/J7TRPDWY6BSAWHBDSV24FN6E'}\n","Grid 2\n","curr_year 2018\n","Saving data for Kishanganj 2018\n","Task Started {'state': 'READY', 'description': 'Kishanganj_2_2018', 'priority': 100, 'creation_timestamp_ms': 1762661428144, 'update_timestamp_ms': 1762661428144, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'BUOJ7O7EF34JDCMOEVJSN2B7', 'name': 'projects/ext-datasets/operations/BUOJ7O7EF34JDCMOEVJSN2B7'}\n","Grid 3\n","curr_year 2018\n","Saving data for Kishanganj 2018\n","Task Started {'state': 'READY', 'description': 'Kishanganj_3_2018', 'priority': 100, 'creation_timestamp_ms': 1762661432097, 'update_timestamp_ms': 1762661432097, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '2MN4L7QBRR2JYQDFGCHRCNIV', 'name': 'projects/ext-datasets/operations/2MN4L7QBRR2JYQDFGCHRCNIV'}\n","Grid 4\n","curr_year 2018\n","Saving data for Kishanganj 2018\n","Task Started {'state': 'READY', 'description': 'Kishanganj_4_2018', 'priority': 100, 'creation_timestamp_ms': 1762661441672, 'update_timestamp_ms': 1762661441672, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'XFPIJKAZOPHDNLTCO667NCMM', 'name': 'projects/ext-datasets/operations/XFPIJKAZOPHDNLTCO667NCMM'}\n","Grid 5\n","curr_year 2018\n","Saving data for Kishanganj 2018\n","Task Started {'state': 'READY', 'description': 'Kishanganj_5_2018', 'priority': 100, 'creation_timestamp_ms': 1762661446896, 'update_timestamp_ms': 1762661446896, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'E5MGGGYFNMJFD3CQG77ZXZVN', 'name': 'projects/ext-datasets/operations/E5MGGGYFNMJFD3CQG77ZXZVN'}\n","Grid 6\n","curr_year 2018\n","Saving data for Kishanganj 2018\n","Task Started {'state': 'READY', 'description': 'Kishanganj_6_2018', 'priority': 100, 'creation_timestamp_ms': 1762661454885, 'update_timestamp_ms': 1762661454885, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '2RMT6KWPNLRPZK6UHA5CZV4G', 'name': 'projects/ext-datasets/operations/2RMT6KWPNLRPZK6UHA5CZV4G'}\n","Grid 7\n","curr_year 2018\n","Saving data for Kishanganj 2018\n","Task Started {'state': 'READY', 'description': 'Kishanganj_7_2018', 'priority': 100, 'creation_timestamp_ms': 1762661461899, 'update_timestamp_ms': 1762661461899, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'Y3DACYNR4M7LWTQBJZTSHSMM', 'name': 'projects/ext-datasets/operations/Y3DACYNR4M7LWTQBJZTSHSMM'}\n","Grid 8\n","curr_year 2018\n","Saving data for Kishanganj 2018\n","Task Started {'state': 'READY', 'description': 'Kishanganj_8_2018', 'priority': 100, 'creation_timestamp_ms': 1762661465511, 'update_timestamp_ms': 1762661465511, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'IFY7YVTGEWUCBE23HC6MBRTJ', 'name': 'projects/ext-datasets/operations/IFY7YVTGEWUCBE23HC6MBRTJ'}\n","Grid 9\n","curr_year 2018\n","Saving data for Kishanganj 2018\n","Task Started {'state': 'READY', 'description': 'Kishanganj_9_2018', 'priority': 100, 'creation_timestamp_ms': 1762661472919, 'update_timestamp_ms': 1762661472919, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'BSACOQVJ5JRL55WG3XCZKURD', 'name': 'projects/ext-datasets/operations/BSACOQVJ5JRL55WG3XCZKURD'}\n","234.68766856193542\n","Year 2018, District 2: Purnia, grids: 1\n","Grid 0\n","curr_year 2018\n","Saving data for Purnia 2018\n","Task Started {'state': 'READY', 'description': 'Purnia_0_2018', 'priority': 100, 'creation_timestamp_ms': 1762661485177, 'update_timestamp_ms': 1762661485177, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'LM7HP5TLZDFEPZ5JILM3GOTQ', 'name': 'projects/ext-datasets/operations/LM7HP5TLZDFEPZ5JILM3GOTQ'}\n","247.0161051750183\n","Year 2018, District 3: Dhanbad, grids: 1\n","Grid 0\n","curr_year 2018\n","Saving data for Dhanbad 2018\n","Task Started {'state': 'READY', 'description': 'Dhanbad_0_2018', 'priority': 100, 'creation_timestamp_ms': 1762661499029, 'update_timestamp_ms': 1762661499029, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'RRQEKBZFOZAUYQGOOFRTJZKW', 'name': 'projects/ext-datasets/operations/RRQEKBZFOZAUYQGOOFRTJZKW'}\n","260.836279630661\n","Year 2018, District 4: Dumka, grids: 10\n","Grid 0\n","curr_year 2018\n","Saving data for Dumka 2018\n","Task Started {'state': 'READY', 'description': 'Dumka_0_2018', 'priority': 100, 'creation_timestamp_ms': 1762661514902, 'update_timestamp_ms': 1762661514902, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'QOHMMPKJFBL62RJZT34C2KWO', 'name': 'projects/ext-datasets/operations/QOHMMPKJFBL62RJZT34C2KWO'}\n","Grid 1\n","curr_year 2018\n","Saving data for Dumka 2018\n","Task Started {'state': 'READY', 'description': 'Dumka_1_2018', 'priority': 100, 'creation_timestamp_ms': 1762661518980, 'update_timestamp_ms': 1762661518980, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'WUDELHZTOL7ULYA6E6BAMJCG', 'name': 'projects/ext-datasets/operations/WUDELHZTOL7ULYA6E6BAMJCG'}\n","Grid 2\n","curr_year 2018\n","Saving data for Dumka 2018\n","Task Started {'state': 'READY', 'description': 'Dumka_2_2018', 'priority': 100, 'creation_timestamp_ms': 1762661530802, 'update_timestamp_ms': 1762661530802, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'Y22SNWU3GELIZSVXNANU6T7T', 'name': 'projects/ext-datasets/operations/Y22SNWU3GELIZSVXNANU6T7T'}\n","Grid 3\n","curr_year 2018\n","Saving data for Dumka 2018\n","Task Started {'state': 'READY', 'description': 'Dumka_3_2018', 'priority': 100, 'creation_timestamp_ms': 1762661538964, 'update_timestamp_ms': 1762661538964, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'NEVS5TJX4XXIFSUUR2X5XBRD', 'name': 'projects/ext-datasets/operations/NEVS5TJX4XXIFSUUR2X5XBRD'}\n","Grid 4\n","curr_year 2018\n","Saving data for Dumka 2018\n","Task Started {'state': 'READY', 'description': 'Dumka_4_2018', 'priority': 100, 'creation_timestamp_ms': 1762661545993, 'update_timestamp_ms': 1762661545993, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'PEFVP6IGH6UZZW4OUNRWHONY', 'name': 'projects/ext-datasets/operations/PEFVP6IGH6UZZW4OUNRWHONY'}\n","Grid 5\n","curr_year 2018\n","Saving data for Dumka 2018\n","Task Started {'state': 'READY', 'description': 'Dumka_5_2018', 'priority': 100, 'creation_timestamp_ms': 1762661552503, 'update_timestamp_ms': 1762661552503, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '4GHVZWFUEPUIF26V7XEFCLXS', 'name': 'projects/ext-datasets/operations/4GHVZWFUEPUIF26V7XEFCLXS'}\n","Grid 6\n","curr_year 2018\n","Saving data for Dumka 2018\n","Task Started {'state': 'READY', 'description': 'Dumka_6_2018', 'priority': 100, 'creation_timestamp_ms': 1762661561607, 'update_timestamp_ms': 1762661561607, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'W4JBXELP2EUVFLWBYAGIOHBU', 'name': 'projects/ext-datasets/operations/W4JBXELP2EUVFLWBYAGIOHBU'}\n","Grid 7\n","curr_year 2018\n","Saving data for Dumka 2018\n","Task Started {'state': 'READY', 'description': 'Dumka_7_2018', 'priority': 100, 'creation_timestamp_ms': 1762661572246, 'update_timestamp_ms': 1762661572246, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'TUCB6WINJJKOI4VLONISQKRE', 'name': 'projects/ext-datasets/operations/TUCB6WINJJKOI4VLONISQKRE'}\n","Grid 8\n","curr_year 2018\n","Saving data for Dumka 2018\n","Task Started {'state': 'READY', 'description': 'Dumka_8_2018', 'priority': 100, 'creation_timestamp_ms': 1762661579463, 'update_timestamp_ms': 1762661579463, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'KZTFPY63GXGI3H7VTPYAYUR6', 'name': 'projects/ext-datasets/operations/KZTFPY63GXGI3H7VTPYAYUR6'}\n","Grid 9\n","curr_year 2018\n","Saving data for Dumka 2018\n","Task Started {'state': 'READY', 'description': 'Dumka_9_2018', 'priority': 100, 'creation_timestamp_ms': 1762661587387, 'update_timestamp_ms': 1762661587387, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '2Q4RZNFEG6UUONYB2X732F2U', 'name': 'projects/ext-datasets/operations/2Q4RZNFEG6UUONYB2X732F2U'}\n","349.2098460197449\n","Year 2018, District 5: Jamtara, grids: 10\n","Grid 0\n","curr_year 2018\n","Saving data for Jamtara 2018\n","Task Started {'state': 'READY', 'description': 'Jamtara_0_2018', 'priority': 100, 'creation_timestamp_ms': 1762661600247, 'update_timestamp_ms': 1762661600247, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'YJXDMS2BXKXQ2L23P7KNW7XZ', 'name': 'projects/ext-datasets/operations/YJXDMS2BXKXQ2L23P7KNW7XZ'}\n","Grid 1\n","curr_year 2018\n","Saving data for Jamtara 2018\n","Task Started {'state': 'READY', 'description': 'Jamtara_1_2018', 'priority': 100, 'creation_timestamp_ms': 1762661606632, 'update_timestamp_ms': 1762661606632, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'T5G3XPYJXY5QKXTEA7ZR25GS', 'name': 'projects/ext-datasets/operations/T5G3XPYJXY5QKXTEA7ZR25GS'}\n","Grid 2\n","curr_year 2018\n","Saving data for Jamtara 2018\n","Task Started {'state': 'READY', 'description': 'Jamtara_2_2018', 'priority': 100, 'creation_timestamp_ms': 1762661613674, 'update_timestamp_ms': 1762661613674, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'ZD2QQCCV2JDR5BIHPHBQ7IE7', 'name': 'projects/ext-datasets/operations/ZD2QQCCV2JDR5BIHPHBQ7IE7'}\n","Grid 3\n","curr_year 2018\n","Saving data for Jamtara 2018\n","Task Started {'state': 'READY', 'description': 'Jamtara_3_2018', 'priority': 100, 'creation_timestamp_ms': 1762661618531, 'update_timestamp_ms': 1762661618531, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'MHWU4GXXNN72JNKE22DR6YGW', 'name': 'projects/ext-datasets/operations/MHWU4GXXNN72JNKE22DR6YGW'}\n","Grid 4\n","curr_year 2018\n","Saving data for Jamtara 2018\n","Task Started {'state': 'READY', 'description': 'Jamtara_4_2018', 'priority': 100, 'creation_timestamp_ms': 1762661624769, 'update_timestamp_ms': 1762661624769, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'CST5KGZVVXEBKR2HKWIY7AKU', 'name': 'projects/ext-datasets/operations/CST5KGZVVXEBKR2HKWIY7AKU'}\n","Grid 5\n","curr_year 2018\n","Saving data for Jamtara 2018\n","Task Started {'state': 'READY', 'description': 'Jamtara_5_2018', 'priority': 100, 'creation_timestamp_ms': 1762661633742, 'update_timestamp_ms': 1762661633742, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'ONT23X7JJYEXWUHRKMZ2GZ35', 'name': 'projects/ext-datasets/operations/ONT23X7JJYEXWUHRKMZ2GZ35'}\n","Grid 6\n","curr_year 2018\n","Saving data for Jamtara 2018\n","Task Started {'state': 'READY', 'description': 'Jamtara_6_2018', 'priority': 100, 'creation_timestamp_ms': 1762661639755, 'update_timestamp_ms': 1762661639755, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'X6KMZH673OJ5AX24CK3EB2QK', 'name': 'projects/ext-datasets/operations/X6KMZH673OJ5AX24CK3EB2QK'}\n","Grid 7\n","curr_year 2018\n","Saving data for Jamtara 2018\n","Task Started {'state': 'READY', 'description': 'Jamtara_7_2018', 'priority': 100, 'creation_timestamp_ms': 1762661647080, 'update_timestamp_ms': 1762661647080, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '7RHETXCLH4HDS2MEHSLAHEMJ', 'name': 'projects/ext-datasets/operations/7RHETXCLH4HDS2MEHSLAHEMJ'}\n","Grid 8\n","curr_year 2018\n","Saving data for Jamtara 2018\n","Task Started {'state': 'READY', 'description': 'Jamtara_8_2018', 'priority': 100, 'creation_timestamp_ms': 1762661653205, 'update_timestamp_ms': 1762661653205, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'ZRHL4I7DKZ6SBAW5PQPZ4YE4', 'name': 'projects/ext-datasets/operations/ZRHL4I7DKZ6SBAW5PQPZ4YE4'}\n","Grid 9\n","curr_year 2018\n","Saving data for Jamtara 2018\n","Task Started {'state': 'READY', 'description': 'Jamtara_9_2018', 'priority': 100, 'creation_timestamp_ms': 1762661660269, 'update_timestamp_ms': 1762661660269, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'X3FZARIGPQAZS47Z2IR2TAZ7', 'name': 'projects/ext-datasets/operations/X3FZARIGPQAZS47Z2IR2TAZ7'}\n","422.12399768829346\n","Year 2018, District 6: Pakur, grids: 9\n","Grid 0\n","curr_year 2018\n","Saving data for Pakur 2018\n","Task Started {'state': 'READY', 'description': 'Pakur_0_2018', 'priority': 100, 'creation_timestamp_ms': 1762661674965, 'update_timestamp_ms': 1762661674965, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'E7QLVYQOYAQJ3B5GOVQ6YYGB', 'name': 'projects/ext-datasets/operations/E7QLVYQOYAQJ3B5GOVQ6YYGB'}\n","Grid 1\n","curr_year 2018\n","Saving data for Pakur 2018\n","Task Started {'state': 'READY', 'description': 'Pakur_1_2018', 'priority': 100, 'creation_timestamp_ms': 1762661682558, 'update_timestamp_ms': 1762661682558, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '7B5AQSQKKOSPEXVXYXS6PC25', 'name': 'projects/ext-datasets/operations/7B5AQSQKKOSPEXVXYXS6PC25'}\n","Grid 2\n","curr_year 2018\n","Saving data for Pakur 2018\n","Task Started {'state': 'READY', 'description': 'Pakur_2_2018', 'priority': 100, 'creation_timestamp_ms': 1762661690346, 'update_timestamp_ms': 1762661690346, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'UNZA2L3HLU6URA3K7HDTPPVP', 'name': 'projects/ext-datasets/operations/UNZA2L3HLU6URA3K7HDTPPVP'}\n","Grid 3\n","curr_year 2018\n","Saving data for Pakur 2018\n","Task Started {'state': 'READY', 'description': 'Pakur_3_2018', 'priority': 100, 'creation_timestamp_ms': 1762661697664, 'update_timestamp_ms': 1762661697664, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'MT66LGWDTK4UX7DMVMUU5SS2', 'name': 'projects/ext-datasets/operations/MT66LGWDTK4UX7DMVMUU5SS2'}\n","Grid 4\n","curr_year 2018\n","Saving data for Pakur 2018\n","Task Started {'state': 'READY', 'description': 'Pakur_4_2018', 'priority': 100, 'creation_timestamp_ms': 1762661704964, 'update_timestamp_ms': 1762661704964, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'LBFAI47IEXNQ5TSK2J45QCY2', 'name': 'projects/ext-datasets/operations/LBFAI47IEXNQ5TSK2J45QCY2'}\n","Grid 5\n","curr_year 2018\n","Saving data for Pakur 2018\n","Task Started {'state': 'READY', 'description': 'Pakur_5_2018', 'priority': 100, 'creation_timestamp_ms': 1762661712665, 'update_timestamp_ms': 1762661712665, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'FIULRPUSO3BYRBXQFOLOEQDG', 'name': 'projects/ext-datasets/operations/FIULRPUSO3BYRBXQFOLOEQDG'}\n","Grid 6\n","curr_year 2018\n","Saving data for Pakur 2018\n","Task Started {'state': 'READY', 'description': 'Pakur_6_2018', 'priority': 100, 'creation_timestamp_ms': 1762661720057, 'update_timestamp_ms': 1762661720057, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'ERQPB7GQQGXTWY45ZE6CGYQF', 'name': 'projects/ext-datasets/operations/ERQPB7GQQGXTWY45ZE6CGYQF'}\n","Grid 7\n","curr_year 2018\n","Saving data for Pakur 2018\n","Task Started {'state': 'READY', 'description': 'Pakur_7_2018', 'priority': 100, 'creation_timestamp_ms': 1762661726493, 'update_timestamp_ms': 1762661726493, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'QSLTLOQ7I2UDADZHY6XXYYRK', 'name': 'projects/ext-datasets/operations/QSLTLOQ7I2UDADZHY6XXYYRK'}\n","Grid 8\n","curr_year 2018\n","Saving data for Pakur 2018\n","Task Started {'state': 'READY', 'description': 'Pakur_8_2018', 'priority': 100, 'creation_timestamp_ms': 1762661732594, 'update_timestamp_ms': 1762661732594, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'BMYAEH5TNIX3ZSDJG7QUVLKE', 'name': 'projects/ext-datasets/operations/BMYAEH5TNIX3ZSDJG7QUVLKE'}\n","494.41910696029663\n","Year 2018, District 7: Purbi Singhbhum, grids: 5\n","Grid 0\n","curr_year 2018\n","Saving data for Purbi Singhbhum 2018\n","Task Started {'state': 'READY', 'description': 'Purbi Singhbhum_0_2018', 'priority': 100, 'creation_timestamp_ms': 1762661746443, 'update_timestamp_ms': 1762661746443, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'GSWALEKV4U7XTT44QJN4WEJF', 'name': 'projects/ext-datasets/operations/GSWALEKV4U7XTT44QJN4WEJF'}\n","Grid 1\n","curr_year 2018\n","Saving data for Purbi Singhbhum 2018\n","Task Started {'state': 'READY', 'description': 'Purbi Singhbhum_1_2018', 'priority': 100, 'creation_timestamp_ms': 1762661755192, 'update_timestamp_ms': 1762661755192, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'MW53TQGCIDDQMMB4YP54N2JK', 'name': 'projects/ext-datasets/operations/MW53TQGCIDDQMMB4YP54N2JK'}\n","Grid 2\n","curr_year 2018\n","Saving data for Purbi Singhbhum 2018\n","Task Started {'state': 'READY', 'description': 'Purbi Singhbhum_2_2018', 'priority': 100, 'creation_timestamp_ms': 1762661762624, 'update_timestamp_ms': 1762661762624, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'WKY2SNAKLC746OIREBOGKAAO', 'name': 'projects/ext-datasets/operations/WKY2SNAKLC746OIREBOGKAAO'}\n","Grid 3\n","curr_year 2018\n","Saving data for Purbi Singhbhum 2018\n","Task Started {'state': 'READY', 'description': 'Purbi Singhbhum_3_2018', 'priority': 100, 'creation_timestamp_ms': 1762661770052, 'update_timestamp_ms': 1762661770052, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'DGZRO2CORNNYUFATKIO5PNBT', 'name': 'projects/ext-datasets/operations/DGZRO2CORNNYUFATKIO5PNBT'}\n","Grid 4\n","curr_year 2018\n","Saving data for Purbi Singhbhum 2018\n","Task Started {'state': 'READY', 'description': 'Purbi Singhbhum_4_2018', 'priority': 100, 'creation_timestamp_ms': 1762661774151, 'update_timestamp_ms': 1762661774151, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'RFKUIDHDBBG3WU6C27YSXET4', 'name': 'projects/ext-datasets/operations/RFKUIDHDBBG3WU6C27YSXET4'}\n","535.9720237255096\n","Year 2018, District 8: Sahibganj, grids: 3\n","Grid 0\n","curr_year 2018\n","Saving data for Sahibganj 2018\n","Task Started {'state': 'READY', 'description': 'Sahibganj_0_2018', 'priority': 100, 'creation_timestamp_ms': 1762661786676, 'update_timestamp_ms': 1762661786676, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'OQIR2E6NJ65MF6PGXJWYWN7A', 'name': 'projects/ext-datasets/operations/OQIR2E6NJ65MF6PGXJWYWN7A'}\n","Grid 1\n","curr_year 2018\n","Saving data for Sahibganj 2018\n","Task Started {'state': 'READY', 'description': 'Sahibganj_1_2018', 'priority': 100, 'creation_timestamp_ms': 1762661793641, 'update_timestamp_ms': 1762661793641, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'SFO53APFK6KST2IDAJ4357TS', 'name': 'projects/ext-datasets/operations/SFO53APFK6KST2IDAJ4357TS'}\n","Grid 2\n","curr_year 2018\n","Saving data for Sahibganj 2018\n","Task Started {'state': 'READY', 'description': 'Sahibganj_2_2018', 'priority': 100, 'creation_timestamp_ms': 1762661797304, 'update_timestamp_ms': 1762661797304, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'D5OPSTNUNJUCQVDPLYN4F5D7', 'name': 'projects/ext-datasets/operations/D5OPSTNUNJUCQVDPLYN4F5D7'}\n","559.1519672870636\n","Year 2018, District 9: Baleshwar, grids: 6\n","Grid 0\n","curr_year 2018\n","Saving data for Baleshwar 2018\n","Task Started {'state': 'READY', 'description': 'Baleshwar_0_2018', 'priority': 100, 'creation_timestamp_ms': 1762661811076, 'update_timestamp_ms': 1762661811076, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'TDYSXAPAI3DCCCPVWMQMYKBB', 'name': 'projects/ext-datasets/operations/TDYSXAPAI3DCCCPVWMQMYKBB'}\n","Grid 1\n","curr_year 2018\n","Saving data for Baleshwar 2018\n","Task Started {'state': 'READY', 'description': 'Baleshwar_1_2018', 'priority': 100, 'creation_timestamp_ms': 1762661817544, 'update_timestamp_ms': 1762661817544, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'B7WL3G2LPT3AGTBTPUFTR5WV', 'name': 'projects/ext-datasets/operations/B7WL3G2LPT3AGTBTPUFTR5WV'}\n","Grid 2\n","curr_year 2018\n","Saving data for Baleshwar 2018\n","Task Started {'state': 'READY', 'description': 'Baleshwar_2_2018', 'priority': 100, 'creation_timestamp_ms': 1762661824254, 'update_timestamp_ms': 1762661824254, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'B2YI4U7QZLL2EP5WK5K44QX2', 'name': 'projects/ext-datasets/operations/B2YI4U7QZLL2EP5WK5K44QX2'}\n","Grid 3\n","curr_year 2018\n","Saving data for Baleshwar 2018\n","Task Started {'state': 'READY', 'description': 'Baleshwar_3_2018', 'priority': 100, 'creation_timestamp_ms': 1762661831304, 'update_timestamp_ms': 1762661831304, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'Y7SNA5MEAGB7OVYCJWYGQBD4', 'name': 'projects/ext-datasets/operations/Y7SNA5MEAGB7OVYCJWYGQBD4'}\n","Grid 4\n","curr_year 2018\n","Saving data for Baleshwar 2018\n","Task Started {'state': 'READY', 'description': 'Baleshwar_4_2018', 'priority': 100, 'creation_timestamp_ms': 1762661836824, 'update_timestamp_ms': 1762661836824, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'VWSA2I55M76LO5CYQRSNPGRZ', 'name': 'projects/ext-datasets/operations/VWSA2I55M76LO5CYQRSNPGRZ'}\n","Grid 5\n","curr_year 2018\n","Saving data for Baleshwar 2018\n","Task Started {'state': 'READY', 'description': 'Baleshwar_5_2018', 'priority': 100, 'creation_timestamp_ms': 1762661844389, 'update_timestamp_ms': 1762661844389, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '2NP4JS264MKWYZS3M76QIJO5', 'name': 'projects/ext-datasets/operations/2NP4JS264MKWYZS3M76QIJO5'}\n","606.1991579532623\n","Year 2018, District 10: Mayurbhanj, grids: 1\n","Grid 0\n","curr_year 2018\n","Saving data for Mayurbhanj 2018\n","Task Started {'state': 'READY', 'description': 'Mayurbhanj_0_2018', 'priority': 100, 'creation_timestamp_ms': 1762661856879, 'update_timestamp_ms': 1762661856879, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'R5CLPSQU5NDLKRR325ZAUNXC', 'name': 'projects/ext-datasets/operations/R5CLPSQU5NDLKRR325ZAUNXC'}\n","618.6984307765961\n","Year 2018, District 11: Bankura, grids: 103\n","Grid 0\n","curr_year 2018\n","Saving data for Bankura 2018\n","Task Started {'state': 'READY', 'description': 'Bankura_0_2018', 'priority': 100, 'creation_timestamp_ms': 1762661869759, 'update_timestamp_ms': 1762661869759, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'STAYYI6TSZPO5B4SQ6VC75WZ', 'name': 'projects/ext-datasets/operations/STAYYI6TSZPO5B4SQ6VC75WZ'}\n","Grid 1\n","curr_year 2018\n","Saving data for Bankura 2018\n","Task Started {'state': 'READY', 'description': 'Bankura_1_2018', 'priority': 100, 'creation_timestamp_ms': 1762661876752, 'update_timestamp_ms': 1762661876752, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '72IZWFUBEQ7A65W3ZWXIRW3G', 'name': 'projects/ext-datasets/operations/72IZWFUBEQ7A65W3ZWXIRW3G'}\n","Grid 2\n","curr_year 2018\n","Saving data for Bankura 2018\n","Task Started {'state': 'READY', 'description': 'Bankura_2_2018', 'priority': 100, 'creation_timestamp_ms': 1762661883727, 'update_timestamp_ms': 1762661883727, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'ZRN6K3MXNMIWACKXAPDLDXJC', 'name': 'projects/ext-datasets/operations/ZRN6K3MXNMIWACKXAPDLDXJC'}\n","Grid 3\n","curr_year 2018\n","Saving data for Bankura 2018\n","Task Started {'state': 'READY', 'description': 'Bankura_3_2018', 'priority': 100, 'creation_timestamp_ms': 1762661894436, 'update_timestamp_ms': 1762661894436, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'UCNY4CKHOYZXT453ZSL73MY5', 'name': 'projects/ext-datasets/operations/UCNY4CKHOYZXT453ZSL73MY5'}\n","Grid 4\n","curr_year 2018\n","Saving data for Bankura 2018\n","Task Started {'state': 'READY', 'description': 'Bankura_4_2018', 'priority': 100, 'creation_timestamp_ms': 1762661906210, 'update_timestamp_ms': 1762661906210, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'MYRHJGIENHTG5AU4ES6WRCLC', 'name': 'projects/ext-datasets/operations/MYRHJGIENHTG5AU4ES6WRCLC'}\n","Grid 5\n","curr_year 2018\n","Saving data for Bankura 2018\n","Task Started {'state': 'READY', 'description': 'Bankura_5_2018', 'priority': 100, 'creation_timestamp_ms': 1762661914342, 'update_timestamp_ms': 1762661914342, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'LLZCTPRO3HBFQL5YWECLYMBP', 'name': 'projects/ext-datasets/operations/LLZCTPRO3HBFQL5YWECLYMBP'}\n","Grid 6\n","curr_year 2018\n","Saving data for Bankura 2018\n","Task Started {'state': 'READY', 'description': 'Bankura_6_2018', 'priority': 100, 'creation_timestamp_ms': 1762661922702, 'update_timestamp_ms': 1762661922702, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'JN5IV7RAI5ZBV26EWSS2UGJ2', 'name': 'projects/ext-datasets/operations/JN5IV7RAI5ZBV26EWSS2UGJ2'}\n","Grid 7\n","curr_year 2018\n","Saving data for Bankura 2018\n","Task Started {'state': 'READY', 'description': 'Bankura_7_2018', 'priority': 100, 'creation_timestamp_ms': 1762661930590, 'update_timestamp_ms': 1762661930590, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'A3CAKZIZEU2SKK7CPHJ5DU3K', 'name': 'projects/ext-datasets/operations/A3CAKZIZEU2SKK7CPHJ5DU3K'}\n","Grid 8\n","curr_year 2018\n","Saving data for Bankura 2018\n","Task Started {'state': 'READY', 'description': 'Bankura_8_2018', 'priority': 100, 'creation_timestamp_ms': 1762661940164, 'update_timestamp_ms': 1762661940164, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'TUV5FFETPUUO7GV4MPAITY64', 'name': 'projects/ext-datasets/operations/TUV5FFETPUUO7GV4MPAITY64'}\n","Grid 9\n","curr_year 2018\n","Saving data for Bankura 2018\n","Task Started {'state': 'READY', 'description': 'Bankura_9_2018', 'priority': 100, 'creation_timestamp_ms': 1762661948118, 'update_timestamp_ms': 1762661948118, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'O5DGHSRQLFSQVSYZJL6C4RFA', 'name': 'projects/ext-datasets/operations/O5DGHSRQLFSQVSYZJL6C4RFA'}\n","Grid 10\n","curr_year 2018\n","Saving data for Bankura 2018\n","Task Started {'state': 'READY', 'description': 'Bankura_10_2018', 'priority': 100, 'creation_timestamp_ms': 1762661955447, 'update_timestamp_ms': 1762661955447, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'XBLOKBW7CZ3DQHPHQVTTNZ7B', 'name': 'projects/ext-datasets/operations/XBLOKBW7CZ3DQHPHQVTTNZ7B'}\n","Grid 11\n","curr_year 2018\n","Saving data for Bankura 2018\n","Task Started {'state': 'READY', 'description': 'Bankura_11_2018', 'priority': 100, 'creation_timestamp_ms': 1762661965446, 'update_timestamp_ms': 1762661965446, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '7B7LSH3Q5TGUJZXWCZFEUDR6', 'name': 'projects/ext-datasets/operations/7B7LSH3Q5TGUJZXWCZFEUDR6'}\n","Grid 12\n","curr_year 2018\n","Saving data for Bankura 2018\n","Task Started {'state': 'READY', 'description': 'Bankura_12_2018', 'priority': 100, 'creation_timestamp_ms': 1762661971465, 'update_timestamp_ms': 1762661971465, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'AG7MEOWAL22OZPTNULN4JVAL', 'name': 'projects/ext-datasets/operations/AG7MEOWAL22OZPTNULN4JVAL'}\n","Grid 13\n","curr_year 2018\n","Saving data for Bankura 2018\n","Task Started {'state': 'READY', 'description': 'Bankura_13_2018', 'priority': 100, 'creation_timestamp_ms': 1762661982035, 'update_timestamp_ms': 1762661982035, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '6OIFXZZ7S5OWH5T26Y7TUDH2', 'name': 'projects/ext-datasets/operations/6OIFXZZ7S5OWH5T26Y7TUDH2'}\n","Grid 14\n","curr_year 2018\n","Saving data for Bankura 2018\n","Task Started {'state': 'READY', 'description': 'Bankura_14_2018', 'priority': 100, 'creation_timestamp_ms': 1762661987865, 'update_timestamp_ms': 1762661987865, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'LBZIJZUR2GC67OYO226JFC5S', 'name': 'projects/ext-datasets/operations/LBZIJZUR2GC67OYO226JFC5S'}\n","Grid 15\n","curr_year 2018\n","Saving data for Bankura 2018\n","Task Started {'state': 'READY', 'description': 'Bankura_15_2018', 'priority': 100, 'creation_timestamp_ms': 1762661996872, 'update_timestamp_ms': 1762661996872, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'IJG5VRFWREKO3OA73EAYGFQH', 'name': 'projects/ext-datasets/operations/IJG5VRFWREKO3OA73EAYGFQH'}\n","Grid 16\n","curr_year 2018\n","Saving data for Bankura 2018\n","Task Started {'state': 'READY', 'description': 'Bankura_16_2018', 'priority': 100, 'creation_timestamp_ms': 1762662002369, 'update_timestamp_ms': 1762662002369, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'IHQZ4Q3HVNEI3HRRVBHYQ5UU', 'name': 'projects/ext-datasets/operations/IHQZ4Q3HVNEI3HRRVBHYQ5UU'}\n","Grid 17\n","curr_year 2018\n","Saving data for Bankura 2018\n","Task Started {'state': 'READY', 'description': 'Bankura_17_2018', 'priority': 100, 'creation_timestamp_ms': 1762662010331, 'update_timestamp_ms': 1762662010331, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'VDVXED3MPPGPPC5TC5OBBDCU', 'name': 'projects/ext-datasets/operations/VDVXED3MPPGPPC5TC5OBBDCU'}\n","Grid 18\n","curr_year 2018\n","Saving data for Bankura 2018\n","Task Started {'state': 'READY', 'description': 'Bankura_18_2018', 'priority': 100, 'creation_timestamp_ms': 1762662017478, 'update_timestamp_ms': 1762662017478, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'CWL6AQYBJIE2Y375UN75KCMF', 'name': 'projects/ext-datasets/operations/CWL6AQYBJIE2Y375UN75KCMF'}\n","Grid 19\n","curr_year 2018\n","Saving data for Bankura 2018\n","Task Started {'state': 'READY', 'description': 'Bankura_19_2018', 'priority': 100, 'creation_timestamp_ms': 1762662025437, 'update_timestamp_ms': 1762662025437, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'ZR5ETYBGZBIQMS2MIAFOCKMH', 'name': 'projects/ext-datasets/operations/ZR5ETYBGZBIQMS2MIAFOCKMH'}\n","Grid 20\n","curr_year 2018\n","Saving data for Bankura 2018\n","Task Started {'state': 'READY', 'description': 'Bankura_20_2018', 'priority': 100, 'creation_timestamp_ms': 1762662032211, 'update_timestamp_ms': 1762662032211, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '2R2QB75376OUY2AEW4NUKVEK', 'name': 'projects/ext-datasets/operations/2R2QB75376OUY2AEW4NUKVEK'}\n","Grid 21\n","curr_year 2018\n","Saving data for Bankura 2018\n","Task Started {'state': 'READY', 'description': 'Bankura_21_2018', 'priority': 100, 'creation_timestamp_ms': 1762662039260, 'update_timestamp_ms': 1762662039260, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '5UECIWMSVBETOS5XAGANZ6UY', 'name': 'projects/ext-datasets/operations/5UECIWMSVBETOS5XAGANZ6UY'}\n","Grid 22\n","curr_year 2018\n","Saving data for Bankura 2018\n","Task Started {'state': 'READY', 'description': 'Bankura_22_2018', 'priority': 100, 'creation_timestamp_ms': 1762662046370, 'update_timestamp_ms': 1762662046370, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'XLMQ5HTF43J5UJMA7PDMITTY', 'name': 'projects/ext-datasets/operations/XLMQ5HTF43J5UJMA7PDMITTY'}\n","Grid 23\n","curr_year 2018\n","Saving data for Bankura 2018\n","Task Started {'state': 'READY', 'description': 'Bankura_23_2018', 'priority': 100, 'creation_timestamp_ms': 1762662054979, 'update_timestamp_ms': 1762662054979, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'EC2L2OPTK7BHK4T4ETPAMXAT', 'name': 'projects/ext-datasets/operations/EC2L2OPTK7BHK4T4ETPAMXAT'}\n","Grid 24\n","curr_year 2018\n","Saving data for Bankura 2018\n","Task Started {'state': 'READY', 'description': 'Bankura_24_2018', 'priority': 100, 'creation_timestamp_ms': 1762662063110, 'update_timestamp_ms': 1762662063110, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'SKCHOBTGXB7AUZR5MDMYBAQ3', 'name': 'projects/ext-datasets/operations/SKCHOBTGXB7AUZR5MDMYBAQ3'}\n","Grid 25\n","curr_year 2018\n","Saving data for Bankura 2018\n","Task Started {'state': 'READY', 'description': 'Bankura_25_2018', 'priority': 100, 'creation_timestamp_ms': 1762662073842, 'update_timestamp_ms': 1762662073842, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'LNQH3EJVOR4FVPS7CHED6AB6', 'name': 'projects/ext-datasets/operations/LNQH3EJVOR4FVPS7CHED6AB6'}\n","Grid 26\n","curr_year 2018\n","Saving data for Bankura 2018\n","Task Started {'state': 'READY', 'description': 'Bankura_26_2018', 'priority': 100, 'creation_timestamp_ms': 1762662081995, 'update_timestamp_ms': 1762662081995, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '6N4Q4XDT2BELBHT64FNLB4SP', 'name': 'projects/ext-datasets/operations/6N4Q4XDT2BELBHT64FNLB4SP'}\n","Grid 27\n","curr_year 2018\n","Saving data for Bankura 2018\n","Task Started {'state': 'READY', 'description': 'Bankura_27_2018', 'priority': 100, 'creation_timestamp_ms': 1762662089326, 'update_timestamp_ms': 1762662089326, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'WQJ5ZMGWGSJXV4VBS4JE4ALQ', 'name': 'projects/ext-datasets/operations/WQJ5ZMGWGSJXV4VBS4JE4ALQ'}\n","Grid 28\n","curr_year 2018\n","Saving data for Bankura 2018\n","Task Started {'state': 'READY', 'description': 'Bankura_28_2018', 'priority': 100, 'creation_timestamp_ms': 1762662107477, 'update_timestamp_ms': 1762662107477, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'V2AWXUTPUQEKFRSFULUCNE3M', 'name': 'projects/ext-datasets/operations/V2AWXUTPUQEKFRSFULUCNE3M'}\n","Grid 29\n","curr_year 2018\n","Saving data for Bankura 2018\n","Task Started {'state': 'READY', 'description': 'Bankura_29_2018', 'priority': 100, 'creation_timestamp_ms': 1762662114625, 'update_timestamp_ms': 1762662114625, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'Q5YQYH5WH3QQYGEATZOOKVJD', 'name': 'projects/ext-datasets/operations/Q5YQYH5WH3QQYGEATZOOKVJD'}\n","Grid 30\n","curr_year 2018\n","Saving data for Bankura 2018\n","Task Started {'state': 'READY', 'description': 'Bankura_30_2018', 'priority': 100, 'creation_timestamp_ms': 1762662121341, 'update_timestamp_ms': 1762662121341, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'FPDEWOS5DMAI5N4VOSYA2LLH', 'name': 'projects/ext-datasets/operations/FPDEWOS5DMAI5N4VOSYA2LLH'}\n","Grid 31\n","curr_year 2018\n","Saving data for Bankura 2018\n","Task Started {'state': 'READY', 'description': 'Bankura_31_2018', 'priority': 100, 'creation_timestamp_ms': 1762662131124, 'update_timestamp_ms': 1762662131124, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'M2EGDWT6C3WHCGLEKCPT3TVK', 'name': 'projects/ext-datasets/operations/M2EGDWT6C3WHCGLEKCPT3TVK'}\n","Grid 32\n","curr_year 2018\n","Saving data for Bankura 2018\n","Task Started {'state': 'READY', 'description': 'Bankura_32_2018', 'priority': 100, 'creation_timestamp_ms': 1762662138013, 'update_timestamp_ms': 1762662138013, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'YVX3LIC6VBOVZU23NU7TIA2D', 'name': 'projects/ext-datasets/operations/YVX3LIC6VBOVZU23NU7TIA2D'}\n","Grid 33\n","curr_year 2018\n","Saving data for Bankura 2018\n","Task Started {'state': 'READY', 'description': 'Bankura_33_2018', 'priority': 100, 'creation_timestamp_ms': 1762662146289, 'update_timestamp_ms': 1762662146289, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '3MTP4WO7E3SO3XA44ZR3XZPU', 'name': 'projects/ext-datasets/operations/3MTP4WO7E3SO3XA44ZR3XZPU'}\n","Grid 34\n","curr_year 2018\n","Saving data for Bankura 2018\n","Task Started {'state': 'READY', 'description': 'Bankura_34_2018', 'priority': 100, 'creation_timestamp_ms': 1762662150335, 'update_timestamp_ms': 1762662150335, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'OVQYXE4A4CF2FNTZD6VOLK2U', 'name': 'projects/ext-datasets/operations/OVQYXE4A4CF2FNTZD6VOLK2U'}\n","Grid 35\n","curr_year 2018\n","Saving data for Bankura 2018\n","Task Started {'state': 'READY', 'description': 'Bankura_35_2018', 'priority': 100, 'creation_timestamp_ms': 1762662158557, 'update_timestamp_ms': 1762662158557, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '4G7JMMFDPHV42WUJRG3C3VMP', 'name': 'projects/ext-datasets/operations/4G7JMMFDPHV42WUJRG3C3VMP'}\n","Grid 36\n","curr_year 2018\n","Saving data for Bankura 2018\n","Task Started {'state': 'READY', 'description': 'Bankura_36_2018', 'priority': 100, 'creation_timestamp_ms': 1762662166584, 'update_timestamp_ms': 1762662166584, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '6F5H3VPJ4YUG4KEA4J4DVMCY', 'name': 'projects/ext-datasets/operations/6F5H3VPJ4YUG4KEA4J4DVMCY'}\n","Grid 37\n","curr_year 2018\n","Saving data for Bankura 2018\n","Task Started {'state': 'READY', 'description': 'Bankura_37_2018', 'priority': 100, 'creation_timestamp_ms': 1762662184617, 'update_timestamp_ms': 1762662184617, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'GFXWSLLOAPWVW523H6VW43OV', 'name': 'projects/ext-datasets/operations/GFXWSLLOAPWVW523H6VW43OV'}\n","Grid 38\n","curr_year 2018\n","Saving data for Bankura 2018\n","Task Started {'state': 'READY', 'description': 'Bankura_38_2018', 'priority': 100, 'creation_timestamp_ms': 1762662189413, 'update_timestamp_ms': 1762662189413, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'C36EBKRXVVTKQBL2QP4HQDUW', 'name': 'projects/ext-datasets/operations/C36EBKRXVVTKQBL2QP4HQDUW'}\n","Grid 39\n","curr_year 2018\n","Saving data for Bankura 2018\n","Task Started {'state': 'READY', 'description': 'Bankura_39_2018', 'priority': 100, 'creation_timestamp_ms': 1762662198287, 'update_timestamp_ms': 1762662198287, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'YNDDSFXBTYEFGSVNBHSZJFP5', 'name': 'projects/ext-datasets/operations/YNDDSFXBTYEFGSVNBHSZJFP5'}\n","Grid 40\n","curr_year 2018\n","Saving data for Bankura 2018\n","Task Started {'state': 'READY', 'description': 'Bankura_40_2018', 'priority': 100, 'creation_timestamp_ms': 1762662205602, 'update_timestamp_ms': 1762662205602, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'MJWSGFBY5B6ANDJIHGIPGN7E', 'name': 'projects/ext-datasets/operations/MJWSGFBY5B6ANDJIHGIPGN7E'}\n","Grid 41\n","curr_year 2018\n","Saving data for Bankura 2018\n","Task Started {'state': 'READY', 'description': 'Bankura_41_2018', 'priority': 100, 'creation_timestamp_ms': 1762662213239, 'update_timestamp_ms': 1762662213239, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'DCTYJB3FCOTI4WJ7NEBOMSXC', 'name': 'projects/ext-datasets/operations/DCTYJB3FCOTI4WJ7NEBOMSXC'}\n","Grid 42\n","curr_year 2018\n","Saving data for Bankura 2018\n","Task Started {'state': 'READY', 'description': 'Bankura_42_2018', 'priority': 100, 'creation_timestamp_ms': 1762662221619, 'update_timestamp_ms': 1762662221619, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'FCXANHOQ6REJHDEZBHG7WNCA', 'name': 'projects/ext-datasets/operations/FCXANHOQ6REJHDEZBHG7WNCA'}\n","Grid 43\n","curr_year 2018\n","Saving data for Bankura 2018\n","Task Started {'state': 'READY', 'description': 'Bankura_43_2018', 'priority': 100, 'creation_timestamp_ms': 1762662227723, 'update_timestamp_ms': 1762662227723, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'KB7QEBRVCYJI7JSJJ6XU7WYB', 'name': 'projects/ext-datasets/operations/KB7QEBRVCYJI7JSJJ6XU7WYB'}\n","Grid 44\n","curr_year 2018\n","Saving data for Bankura 2018\n","Task Started {'state': 'READY', 'description': 'Bankura_44_2018', 'priority': 100, 'creation_timestamp_ms': 1762662235717, 'update_timestamp_ms': 1762662235717, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '5M26YN4LL22VBANA4ZGNN22G', 'name': 'projects/ext-datasets/operations/5M26YN4LL22VBANA4ZGNN22G'}\n","Grid 45\n","curr_year 2018\n","Saving data for Bankura 2018\n","Task Started {'state': 'READY', 'description': 'Bankura_45_2018', 'priority': 100, 'creation_timestamp_ms': 1762662243442, 'update_timestamp_ms': 1762662243442, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'UYZWOCUQPRAINGQU5AGWS6HU', 'name': 'projects/ext-datasets/operations/UYZWOCUQPRAINGQU5AGWS6HU'}\n","Grid 46\n","curr_year 2018\n","Saving data for Bankura 2018\n","Task Started {'state': 'READY', 'description': 'Bankura_46_2018', 'priority': 100, 'creation_timestamp_ms': 1762662251072, 'update_timestamp_ms': 1762662251072, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '2HN7JFZ5ZFCTIND4OUKA3M72', 'name': 'projects/ext-datasets/operations/2HN7JFZ5ZFCTIND4OUKA3M72'}\n","Grid 47\n","curr_year 2018\n","Saving data for Bankura 2018\n","Task Started {'state': 'READY', 'description': 'Bankura_47_2018', 'priority': 100, 'creation_timestamp_ms': 1762662255803, 'update_timestamp_ms': 1762662255803, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'M3COZQAWMK27SG3AQNGFZHE6', 'name': 'projects/ext-datasets/operations/M3COZQAWMK27SG3AQNGFZHE6'}\n","Grid 48\n","curr_year 2018\n","Saving data for Bankura 2018\n","Task Started {'state': 'READY', 'description': 'Bankura_48_2018', 'priority': 100, 'creation_timestamp_ms': 1762662261699, 'update_timestamp_ms': 1762662261699, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'NGYDGB7ZGDUGCXLSMBI5MHEG', 'name': 'projects/ext-datasets/operations/NGYDGB7ZGDUGCXLSMBI5MHEG'}\n","Grid 49\n","curr_year 2018\n","Saving data for Bankura 2018\n","Task Started {'state': 'READY', 'description': 'Bankura_49_2018', 'priority': 100, 'creation_timestamp_ms': 1762662270096, 'update_timestamp_ms': 1762662270096, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'YEAPN44AOQZSMMZ4AT3VK3B4', 'name': 'projects/ext-datasets/operations/YEAPN44AOQZSMMZ4AT3VK3B4'}\n","Grid 50\n","curr_year 2018\n","Saving data for Bankura 2018\n","Task Started {'state': 'READY', 'description': 'Bankura_50_2018', 'priority': 100, 'creation_timestamp_ms': 1762662290667, 'update_timestamp_ms': 1762662290667, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'PC7LH7UTGSX3Y33VSUIXHDWY', 'name': 'projects/ext-datasets/operations/PC7LH7UTGSX3Y33VSUIXHDWY'}\n","Grid 51\n","curr_year 2018\n","Saving data for Bankura 2018\n","Task Started {'state': 'READY', 'description': 'Bankura_51_2018', 'priority': 100, 'creation_timestamp_ms': 1762662299690, 'update_timestamp_ms': 1762662299690, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'YRNKM6XCEXVI5E4SVC5QLEGV', 'name': 'projects/ext-datasets/operations/YRNKM6XCEXVI5E4SVC5QLEGV'}\n","Grid 52\n","curr_year 2018\n","Saving data for Bankura 2018\n","Task Started {'state': 'READY', 'description': 'Bankura_52_2018', 'priority': 100, 'creation_timestamp_ms': 1762662305843, 'update_timestamp_ms': 1762662305843, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'OXE6HIN2RFKBCPSJ54XRBJUM', 'name': 'projects/ext-datasets/operations/OXE6HIN2RFKBCPSJ54XRBJUM'}\n","Grid 53\n","curr_year 2018\n","Saving data for Bankura 2018\n","Task Started {'state': 'READY', 'description': 'Bankura_53_2018', 'priority': 100, 'creation_timestamp_ms': 1762662314131, 'update_timestamp_ms': 1762662314131, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'JLGA7DVBZXB4HS2BENXXBXOK', 'name': 'projects/ext-datasets/operations/JLGA7DVBZXB4HS2BENXXBXOK'}\n","Grid 54\n","curr_year 2018\n","Saving data for Bankura 2018\n","Task Started {'state': 'READY', 'description': 'Bankura_54_2018', 'priority': 100, 'creation_timestamp_ms': 1762662320564, 'update_timestamp_ms': 1762662320564, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'HRK3SGO4WFBUMHTWEFYO4SQ4', 'name': 'projects/ext-datasets/operations/HRK3SGO4WFBUMHTWEFYO4SQ4'}\n","Grid 55\n","curr_year 2018\n","Saving data for Bankura 2018\n","Task Started {'state': 'READY', 'description': 'Bankura_55_2018', 'priority': 100, 'creation_timestamp_ms': 1762662328714, 'update_timestamp_ms': 1762662328714, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'RLDODOP4KDUTKXOXMONXQUIK', 'name': 'projects/ext-datasets/operations/RLDODOP4KDUTKXOXMONXQUIK'}\n","Grid 56\n","curr_year 2018\n","Saving data for Bankura 2018\n","Task Started {'state': 'READY', 'description': 'Bankura_56_2018', 'priority': 100, 'creation_timestamp_ms': 1762662336187, 'update_timestamp_ms': 1762662336187, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'D6BLHDKIXDEQMMV6YKUFQJAL', 'name': 'projects/ext-datasets/operations/D6BLHDKIXDEQMMV6YKUFQJAL'}\n","Grid 57\n","curr_year 2018\n","Saving data for Bankura 2018\n","Task Started {'state': 'READY', 'description': 'Bankura_57_2018', 'priority': 100, 'creation_timestamp_ms': 1762662339638, 'update_timestamp_ms': 1762662339638, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'X6NQRBN7LKSGWSC4TS5MEIER', 'name': 'projects/ext-datasets/operations/X6NQRBN7LKSGWSC4TS5MEIER'}\n","Grid 58\n","curr_year 2018\n","Saving data for Bankura 2018\n","Task Started {'state': 'READY', 'description': 'Bankura_58_2018', 'priority': 100, 'creation_timestamp_ms': 1762662345627, 'update_timestamp_ms': 1762662345627, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '6IW4SBZEZN3T7BUBOZXR23ON', 'name': 'projects/ext-datasets/operations/6IW4SBZEZN3T7BUBOZXR23ON'}\n","Grid 59\n","curr_year 2018\n","Saving data for Bankura 2018\n","Task Started {'state': 'READY', 'description': 'Bankura_59_2018', 'priority': 100, 'creation_timestamp_ms': 1762662352081, 'update_timestamp_ms': 1762662352081, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'RHSKEKQO2WB5AFAAOGMFM4U6', 'name': 'projects/ext-datasets/operations/RHSKEKQO2WB5AFAAOGMFM4U6'}\n","Grid 60\n","curr_year 2018\n","Saving data for Bankura 2018\n","Task Started {'state': 'READY', 'description': 'Bankura_60_2018', 'priority': 100, 'creation_timestamp_ms': 1762662357706, 'update_timestamp_ms': 1762662357706, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '7FNZSJ7UQSW5IKMH4CNZPEX4', 'name': 'projects/ext-datasets/operations/7FNZSJ7UQSW5IKMH4CNZPEX4'}\n","Grid 61\n","curr_year 2018\n","Saving data for Bankura 2018\n","Task Started {'state': 'READY', 'description': 'Bankura_61_2018', 'priority': 100, 'creation_timestamp_ms': 1762662366119, 'update_timestamp_ms': 1762662366119, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'NGV3PBO7LJV3B456NN3BUA72', 'name': 'projects/ext-datasets/operations/NGV3PBO7LJV3B456NN3BUA72'}\n","Grid 62\n","curr_year 2018\n","Saving data for Bankura 2018\n","Task Started {'state': 'READY', 'description': 'Bankura_62_2018', 'priority': 100, 'creation_timestamp_ms': 1762662371980, 'update_timestamp_ms': 1762662371980, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '6WCVWZQZWWHNIILUOTRD5Q5B', 'name': 'projects/ext-datasets/operations/6WCVWZQZWWHNIILUOTRD5Q5B'}\n","Grid 63\n","curr_year 2018\n","Saving data for Bankura 2018\n","Task Started {'state': 'READY', 'description': 'Bankura_63_2018', 'priority': 100, 'creation_timestamp_ms': 1762662379126, 'update_timestamp_ms': 1762662379126, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'EDIVZOEFDLZPEVFVNZQRKOFY', 'name': 'projects/ext-datasets/operations/EDIVZOEFDLZPEVFVNZQRKOFY'}\n","Grid 64\n","curr_year 2018\n","Saving data for Bankura 2018\n","Task Started {'state': 'READY', 'description': 'Bankura_64_2018', 'priority': 100, 'creation_timestamp_ms': 1762662384858, 'update_timestamp_ms': 1762662384858, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'R5SKSY3IODVXAPZHFZ5VMRRW', 'name': 'projects/ext-datasets/operations/R5SKSY3IODVXAPZHFZ5VMRRW'}\n","Grid 65\n","curr_year 2018\n","Saving data for Bankura 2018\n","Task Started {'state': 'READY', 'description': 'Bankura_65_2018', 'priority': 100, 'creation_timestamp_ms': 1762662394129, 'update_timestamp_ms': 1762662394129, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '7BJQC7NTRKWRXODRISP4YWUR', 'name': 'projects/ext-datasets/operations/7BJQC7NTRKWRXODRISP4YWUR'}\n","Grid 66\n","curr_year 2018\n","Saving data for Bankura 2018\n","Task Started {'state': 'READY', 'description': 'Bankura_66_2018', 'priority': 100, 'creation_timestamp_ms': 1762662402161, 'update_timestamp_ms': 1762662402161, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '6YYJFW52P7W6WJA6I6W5LVRL', 'name': 'projects/ext-datasets/operations/6YYJFW52P7W6WJA6I6W5LVRL'}\n","Grid 67\n","curr_year 2018\n","Saving data for Bankura 2018\n","Task Started {'state': 'READY', 'description': 'Bankura_67_2018', 'priority': 100, 'creation_timestamp_ms': 1762662410861, 'update_timestamp_ms': 1762662410861, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'WR2VL2QTR44KFUXNVN22RCD5', 'name': 'projects/ext-datasets/operations/WR2VL2QTR44KFUXNVN22RCD5'}\n","Grid 68\n","curr_year 2018\n","Saving data for Bankura 2018\n","Task Started {'state': 'READY', 'description': 'Bankura_68_2018', 'priority': 100, 'creation_timestamp_ms': 1762662414538, 'update_timestamp_ms': 1762662414538, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '7DSU7AISKVNDQ5XHPHTDIL7G', 'name': 'projects/ext-datasets/operations/7DSU7AISKVNDQ5XHPHTDIL7G'}\n","Grid 69\n","curr_year 2018\n","Saving data for Bankura 2018\n","Task Started {'state': 'READY', 'description': 'Bankura_69_2018', 'priority': 100, 'creation_timestamp_ms': 1762662423666, 'update_timestamp_ms': 1762662423666, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '3AQBCBLOEI35ZASVHUVES5W4', 'name': 'projects/ext-datasets/operations/3AQBCBLOEI35ZASVHUVES5W4'}\n","Grid 70\n","curr_year 2018\n","Saving data for Bankura 2018\n","Task Started {'state': 'READY', 'description': 'Bankura_70_2018', 'priority': 100, 'creation_timestamp_ms': 1762662432102, 'update_timestamp_ms': 1762662432102, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'HZEYWT53DCO2FB3GMFMD2ZOM', 'name': 'projects/ext-datasets/operations/HZEYWT53DCO2FB3GMFMD2ZOM'}\n","Grid 71\n","curr_year 2018\n","Saving data for Bankura 2018\n","Task Started {'state': 'READY', 'description': 'Bankura_71_2018', 'priority': 100, 'creation_timestamp_ms': 1762662441628, 'update_timestamp_ms': 1762662441628, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'IXCUHAU3PJ3GUO3RAMWQWWZO', 'name': 'projects/ext-datasets/operations/IXCUHAU3PJ3GUO3RAMWQWWZO'}\n","Grid 72\n","curr_year 2018\n","Saving data for Bankura 2018\n","Task Started {'state': 'READY', 'description': 'Bankura_72_2018', 'priority': 100, 'creation_timestamp_ms': 1762662457917, 'update_timestamp_ms': 1762662457917, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '6MSA2JK6TZZZCBKMWR4ZEDYW', 'name': 'projects/ext-datasets/operations/6MSA2JK6TZZZCBKMWR4ZEDYW'}\n","Grid 73\n","curr_year 2018\n","Saving data for Bankura 2018\n","Task Started {'state': 'READY', 'description': 'Bankura_73_2018', 'priority': 100, 'creation_timestamp_ms': 1762662466701, 'update_timestamp_ms': 1762662466701, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'UNFXUXZDVB5WXDVCJXMT6ZYX', 'name': 'projects/ext-datasets/operations/UNFXUXZDVB5WXDVCJXMT6ZYX'}\n","Grid 74\n","curr_year 2018\n","Saving data for Bankura 2018\n","Task Started {'state': 'READY', 'description': 'Bankura_74_2018', 'priority': 100, 'creation_timestamp_ms': 1762662471448, 'update_timestamp_ms': 1762662471448, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'AISEA5YKT46TYYP4ZUPGWGUU', 'name': 'projects/ext-datasets/operations/AISEA5YKT46TYYP4ZUPGWGUU'}\n","Grid 75\n","curr_year 2018\n","Saving data for Bankura 2018\n","Task Started {'state': 'READY', 'description': 'Bankura_75_2018', 'priority': 100, 'creation_timestamp_ms': 1762662479102, 'update_timestamp_ms': 1762662479102, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'KM7I4S7HQQNCH3FLWAINJBTP', 'name': 'projects/ext-datasets/operations/KM7I4S7HQQNCH3FLWAINJBTP'}\n","Grid 76\n","curr_year 2018\n","Saving data for Bankura 2018\n","Task Started {'state': 'READY', 'description': 'Bankura_76_2018', 'priority': 100, 'creation_timestamp_ms': 1762662485846, 'update_timestamp_ms': 1762662485846, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'HEE447VKHQ3H3HFFOEPNJVVM', 'name': 'projects/ext-datasets/operations/HEE447VKHQ3H3HFFOEPNJVVM'}\n","Grid 77\n","curr_year 2018\n","Saving data for Bankura 2018\n","Task Started {'state': 'READY', 'description': 'Bankura_77_2018', 'priority': 100, 'creation_timestamp_ms': 1762662489539, 'update_timestamp_ms': 1762662489539, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'NWJMXCWHAGTUT6WHE5DZDADQ', 'name': 'projects/ext-datasets/operations/NWJMXCWHAGTUT6WHE5DZDADQ'}\n","Grid 78\n","curr_year 2018\n","Saving data for Bankura 2018\n","Task Started {'state': 'READY', 'description': 'Bankura_78_2018', 'priority': 100, 'creation_timestamp_ms': 1762662493898, 'update_timestamp_ms': 1762662493898, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'FTPH3L7NDYOHSZLKK3DZM5IO', 'name': 'projects/ext-datasets/operations/FTPH3L7NDYOHSZLKK3DZM5IO'}\n","Grid 79\n","curr_year 2018\n","Saving data for Bankura 2018\n","Task Started {'state': 'READY', 'description': 'Bankura_79_2018', 'priority': 100, 'creation_timestamp_ms': 1762662500167, 'update_timestamp_ms': 1762662500167, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'QDLUJWKPXMOUDKW2DKKBPBGN', 'name': 'projects/ext-datasets/operations/QDLUJWKPXMOUDKW2DKKBPBGN'}\n","Grid 80\n","curr_year 2018\n","Saving data for Bankura 2018\n","Task Started {'state': 'READY', 'description': 'Bankura_80_2018', 'priority': 100, 'creation_timestamp_ms': 1762662511569, 'update_timestamp_ms': 1762662511569, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'XWNTVV5AFI737DY6FV5X7K77', 'name': 'projects/ext-datasets/operations/XWNTVV5AFI737DY6FV5X7K77'}\n","Grid 81\n","curr_year 2018\n","Saving data for Bankura 2018\n","Task Started {'state': 'READY', 'description': 'Bankura_81_2018', 'priority': 100, 'creation_timestamp_ms': 1762662521065, 'update_timestamp_ms': 1762662521065, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'LPMFCHNYD3CSSDV36C2UL4MV', 'name': 'projects/ext-datasets/operations/LPMFCHNYD3CSSDV36C2UL4MV'}\n","Grid 82\n","curr_year 2018\n","Saving data for Bankura 2018\n","Task Started {'state': 'READY', 'description': 'Bankura_82_2018', 'priority': 100, 'creation_timestamp_ms': 1762662531383, 'update_timestamp_ms': 1762662531383, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'XZXADYANV7O4Q56UNXPE75IK', 'name': 'projects/ext-datasets/operations/XZXADYANV7O4Q56UNXPE75IK'}\n","Grid 83\n","curr_year 2018\n","Saving data for Bankura 2018\n","Task Started {'state': 'READY', 'description': 'Bankura_83_2018', 'priority': 100, 'creation_timestamp_ms': 1762662548313, 'update_timestamp_ms': 1762662548313, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'V4WMULPTRLR2VRXIBIYBBDDI', 'name': 'projects/ext-datasets/operations/V4WMULPTRLR2VRXIBIYBBDDI'}\n","Grid 84\n","curr_year 2018\n","Saving data for Bankura 2018\n","Task Started {'state': 'READY', 'description': 'Bankura_84_2018', 'priority': 100, 'creation_timestamp_ms': 1762662557900, 'update_timestamp_ms': 1762662557900, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'O4FTHLVZODQWGD5GSV3YFWBN', 'name': 'projects/ext-datasets/operations/O4FTHLVZODQWGD5GSV3YFWBN'}\n","Grid 85\n","curr_year 2018\n","Saving data for Bankura 2018\n","Task Started {'state': 'READY', 'description': 'Bankura_85_2018', 'priority': 100, 'creation_timestamp_ms': 1762662566782, 'update_timestamp_ms': 1762662566782, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '4KC7OIL2XGF2SB2MTKJDDT4Y', 'name': 'projects/ext-datasets/operations/4KC7OIL2XGF2SB2MTKJDDT4Y'}\n","Grid 86\n","curr_year 2018\n","Saving data for Bankura 2018\n","Task Started {'state': 'READY', 'description': 'Bankura_86_2018', 'priority': 100, 'creation_timestamp_ms': 1762662581004, 'update_timestamp_ms': 1762662581004, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'DNHER2AA743EFZEIZTEEAFHD', 'name': 'projects/ext-datasets/operations/DNHER2AA743EFZEIZTEEAFHD'}\n","Grid 87\n","curr_year 2018\n","Saving data for Bankura 2018\n","Task Started {'state': 'READY', 'description': 'Bankura_87_2018', 'priority': 100, 'creation_timestamp_ms': 1762662589639, 'update_timestamp_ms': 1762662589639, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'EBZYKLFZDODZ6DGMP57JG5OD', 'name': 'projects/ext-datasets/operations/EBZYKLFZDODZ6DGMP57JG5OD'}\n","Grid 88\n","curr_year 2018\n","Saving data for Bankura 2018\n","Task Started {'state': 'READY', 'description': 'Bankura_88_2018', 'priority': 100, 'creation_timestamp_ms': 1762662597190, 'update_timestamp_ms': 1762662597190, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '7YVFLQI5JGODS4MSTMUA6JDO', 'name': 'projects/ext-datasets/operations/7YVFLQI5JGODS4MSTMUA6JDO'}\n","Grid 89\n","curr_year 2018\n","Saving data for Bankura 2018\n","Task Started {'state': 'READY', 'description': 'Bankura_89_2018', 'priority': 100, 'creation_timestamp_ms': 1762662608586, 'update_timestamp_ms': 1762662608586, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'OUNFSYOPKEUTECSJKWHHUKQA', 'name': 'projects/ext-datasets/operations/OUNFSYOPKEUTECSJKWHHUKQA'}\n","Grid 90\n","curr_year 2018\n","Saving data for Bankura 2018\n","Task Started {'state': 'READY', 'description': 'Bankura_90_2018', 'priority': 100, 'creation_timestamp_ms': 1762662618337, 'update_timestamp_ms': 1762662618337, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'LX6WCRWHFJCGAQ6YBNYZTYM4', 'name': 'projects/ext-datasets/operations/LX6WCRWHFJCGAQ6YBNYZTYM4'}\n","Grid 91\n","curr_year 2018\n","Saving data for Bankura 2018\n","Task Started {'state': 'READY', 'description': 'Bankura_91_2018', 'priority': 100, 'creation_timestamp_ms': 1762662625221, 'update_timestamp_ms': 1762662625221, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '6U47YJX5BJQ5C5DOO6JEIZUH', 'name': 'projects/ext-datasets/operations/6U47YJX5BJQ5C5DOO6JEIZUH'}\n","Grid 92\n","curr_year 2018\n","Saving data for Bankura 2018\n","Task Started {'state': 'READY', 'description': 'Bankura_92_2018', 'priority': 100, 'creation_timestamp_ms': 1762662632164, 'update_timestamp_ms': 1762662632164, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'YZUUKUDK5PZZ7FRHLMWYDP46', 'name': 'projects/ext-datasets/operations/YZUUKUDK5PZZ7FRHLMWYDP46'}\n","Grid 93\n","curr_year 2018\n","Saving data for Bankura 2018\n","Task Started {'state': 'READY', 'description': 'Bankura_93_2018', 'priority': 100, 'creation_timestamp_ms': 1762662641124, 'update_timestamp_ms': 1762662641124, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'YPF3EA4T6HC4PZSO4DROILIQ', 'name': 'projects/ext-datasets/operations/YPF3EA4T6HC4PZSO4DROILIQ'}\n","Grid 94\n","curr_year 2018\n","Saving data for Bankura 2018\n","Task Started {'state': 'READY', 'description': 'Bankura_94_2018', 'priority': 100, 'creation_timestamp_ms': 1762662649719, 'update_timestamp_ms': 1762662649719, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'X6OZH6FTJ5FTQWHINJV24ZDR', 'name': 'projects/ext-datasets/operations/X6OZH6FTJ5FTQWHINJV24ZDR'}\n","Grid 95\n","curr_year 2018\n","Saving data for Bankura 2018\n","Task Started {'state': 'READY', 'description': 'Bankura_95_2018', 'priority': 100, 'creation_timestamp_ms': 1762662657088, 'update_timestamp_ms': 1762662657088, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'DBIJGLF3F3V3VKQPR4VCWXHC', 'name': 'projects/ext-datasets/operations/DBIJGLF3F3V3VKQPR4VCWXHC'}\n","Grid 96\n","curr_year 2018\n","Saving data for Bankura 2018\n","Task Started {'state': 'READY', 'description': 'Bankura_96_2018', 'priority': 100, 'creation_timestamp_ms': 1762662663908, 'update_timestamp_ms': 1762662663908, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'HYHCQT2NV5ARFMF5FLAXTYX7', 'name': 'projects/ext-datasets/operations/HYHCQT2NV5ARFMF5FLAXTYX7'}\n","Grid 97\n","curr_year 2018\n","Saving data for Bankura 2018\n","Task Started {'state': 'READY', 'description': 'Bankura_97_2018', 'priority': 100, 'creation_timestamp_ms': 1762662672397, 'update_timestamp_ms': 1762662672397, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'YFFOWM2O3E7HBGR26H54Z6B5', 'name': 'projects/ext-datasets/operations/YFFOWM2O3E7HBGR26H54Z6B5'}\n","Grid 98\n","curr_year 2018\n","Saving data for Bankura 2018\n","Task Started {'state': 'READY', 'description': 'Bankura_98_2018', 'priority': 100, 'creation_timestamp_ms': 1762662681198, 'update_timestamp_ms': 1762662681198, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'AZPDE35I5PJWJ5IJIL32IT5T', 'name': 'projects/ext-datasets/operations/AZPDE35I5PJWJ5IJIL32IT5T'}\n","Grid 99\n","curr_year 2018\n","Saving data for Bankura 2018\n","Task Started {'state': 'READY', 'description': 'Bankura_99_2018', 'priority': 100, 'creation_timestamp_ms': 1762662688739, 'update_timestamp_ms': 1762662688739, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'ER376KGTFSYINJ5TLGNKUCNZ', 'name': 'projects/ext-datasets/operations/ER376KGTFSYINJ5TLGNKUCNZ'}\n","Grid 100\n","curr_year 2018\n","Saving data for Bankura 2018\n","Task Started {'state': 'READY', 'description': 'Bankura_100_2018', 'priority': 100, 'creation_timestamp_ms': 1762662691963, 'update_timestamp_ms': 1762662691963, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'J4TM3N3GQZQSZLAA475SN6TH', 'name': 'projects/ext-datasets/operations/J4TM3N3GQZQSZLAA475SN6TH'}\n","Grid 101\n","curr_year 2018\n","Saving data for Bankura 2018\n","Task Started {'state': 'READY', 'description': 'Bankura_101_2018', 'priority': 100, 'creation_timestamp_ms': 1762662700337, 'update_timestamp_ms': 1762662700337, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'JLWJBS3W6UHVJSWAJSL5KVLN', 'name': 'projects/ext-datasets/operations/JLWJBS3W6UHVJSWAJSL5KVLN'}\n","Grid 102\n","curr_year 2018\n","Saving data for Bankura 2018\n","Task Started {'state': 'READY', 'description': 'Bankura_102_2018', 'priority': 100, 'creation_timestamp_ms': 1762662709090, 'update_timestamp_ms': 1762662709090, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'UMYBJM2WLTINTWMYYRX5NWUS', 'name': 'projects/ext-datasets/operations/UMYBJM2WLTINTWMYYRX5NWUS'}\n","1470.9396209716797\n","Year 2018, District 12: Barddhaman, grids: 112\n","Grid 0\n","curr_year 2018\n","Saving data for Barddhaman 2018\n","Task Started {'state': 'READY', 'description': 'Barddhaman_0_2018', 'priority': 100, 'creation_timestamp_ms': 1762662720295, 'update_timestamp_ms': 1762662720295, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'CVYAPFGUFSWB52RYSK5OM4WB', 'name': 'projects/ext-datasets/operations/CVYAPFGUFSWB52RYSK5OM4WB'}\n","Grid 1\n","curr_year 2018\n","Saving data for Barddhaman 2018\n","Task Started {'state': 'READY', 'description': 'Barddhaman_1_2018', 'priority': 100, 'creation_timestamp_ms': 1762662725451, 'update_timestamp_ms': 1762662725451, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'QSF4HUMEERLP4CPQLVKER4WV', 'name': 'projects/ext-datasets/operations/QSF4HUMEERLP4CPQLVKER4WV'}\n","Grid 2\n","curr_year 2018\n","Saving data for Barddhaman 2018\n","Task Started {'state': 'READY', 'description': 'Barddhaman_2_2018', 'priority': 100, 'creation_timestamp_ms': 1762662734484, 'update_timestamp_ms': 1762662734484, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'CVY66EKENE6B4NMH2PJSUFCT', 'name': 'projects/ext-datasets/operations/CVY66EKENE6B4NMH2PJSUFCT'}\n","Grid 3\n","curr_year 2018\n","Saving data for Barddhaman 2018\n","Task Started {'state': 'READY', 'description': 'Barddhaman_3_2018', 'priority': 100, 'creation_timestamp_ms': 1762662741414, 'update_timestamp_ms': 1762662741414, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'YT6ZTJUXFWG5H2MGCEVCUZZQ', 'name': 'projects/ext-datasets/operations/YT6ZTJUXFWG5H2MGCEVCUZZQ'}\n","Grid 4\n","curr_year 2018\n","Saving data for Barddhaman 2018\n","Task Started {'state': 'READY', 'description': 'Barddhaman_4_2018', 'priority': 100, 'creation_timestamp_ms': 1762662749134, 'update_timestamp_ms': 1762662749134, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'COCYHFDUQLAUJYXXD23R44BM', 'name': 'projects/ext-datasets/operations/COCYHFDUQLAUJYXXD23R44BM'}\n","Grid 5\n","curr_year 2018\n","Saving data for Barddhaman 2018\n","Task Started {'state': 'READY', 'description': 'Barddhaman_5_2018', 'priority': 100, 'creation_timestamp_ms': 1762662753867, 'update_timestamp_ms': 1762662753867, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'F5WXMDHXOYC7STHYBR62V7O2', 'name': 'projects/ext-datasets/operations/F5WXMDHXOYC7STHYBR62V7O2'}\n","Grid 6\n","curr_year 2018\n","Saving data for Barddhaman 2018\n","Task Started {'state': 'READY', 'description': 'Barddhaman_6_2018', 'priority': 100, 'creation_timestamp_ms': 1762662759975, 'update_timestamp_ms': 1762662759975, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '776SZ2R2H4PBBQYTTUM2LMOE', 'name': 'projects/ext-datasets/operations/776SZ2R2H4PBBQYTTUM2LMOE'}\n","Grid 7\n","curr_year 2018\n","Saving data for Barddhaman 2018\n","Task Started {'state': 'READY', 'description': 'Barddhaman_7_2018', 'priority': 100, 'creation_timestamp_ms': 1762662767571, 'update_timestamp_ms': 1762662767571, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'ZV5M5TW7V6RDUGJUNLMPUC2C', 'name': 'projects/ext-datasets/operations/ZV5M5TW7V6RDUGJUNLMPUC2C'}\n","Grid 8\n","curr_year 2018\n","Saving data for Barddhaman 2018\n","Task Started {'state': 'READY', 'description': 'Barddhaman_8_2018', 'priority': 100, 'creation_timestamp_ms': 1762662772693, 'update_timestamp_ms': 1762662772693, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '5LJZRLN5EUN32TUMHM5Z6S6U', 'name': 'projects/ext-datasets/operations/5LJZRLN5EUN32TUMHM5Z6S6U'}\n","Grid 9\n","curr_year 2018\n","Saving data for Barddhaman 2018\n","Task Started {'state': 'READY', 'description': 'Barddhaman_9_2018', 'priority': 100, 'creation_timestamp_ms': 1762662778997, 'update_timestamp_ms': 1762662778997, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'WQH7D6YHAGG2TK43TR427DXA', 'name': 'projects/ext-datasets/operations/WQH7D6YHAGG2TK43TR427DXA'}\n","Grid 10\n","curr_year 2018\n","Saving data for Barddhaman 2018\n","Task Started {'state': 'READY', 'description': 'Barddhaman_10_2018', 'priority': 100, 'creation_timestamp_ms': 1762662786704, 'update_timestamp_ms': 1762662786704, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'O2OF6Y3FXIIOZMQJYAVTDB4T', 'name': 'projects/ext-datasets/operations/O2OF6Y3FXIIOZMQJYAVTDB4T'}\n","Grid 11\n","curr_year 2018\n","Saving data for Barddhaman 2018\n","Task Started {'state': 'READY', 'description': 'Barddhaman_11_2018', 'priority': 100, 'creation_timestamp_ms': 1762662793594, 'update_timestamp_ms': 1762662793594, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'CUNYJ6XVKOK5JWNBM3PXPKII', 'name': 'projects/ext-datasets/operations/CUNYJ6XVKOK5JWNBM3PXPKII'}\n","Grid 12\n","curr_year 2018\n","Saving data for Barddhaman 2018\n","Task Started {'state': 'READY', 'description': 'Barddhaman_12_2018', 'priority': 100, 'creation_timestamp_ms': 1762662797432, 'update_timestamp_ms': 1762662797432, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'ZY42XPHLSJJTASYEPK3HWTNB', 'name': 'projects/ext-datasets/operations/ZY42XPHLSJJTASYEPK3HWTNB'}\n","Grid 13\n","curr_year 2018\n","Saving data for Barddhaman 2018\n","Task Started {'state': 'READY', 'description': 'Barddhaman_13_2018', 'priority': 100, 'creation_timestamp_ms': 1762662805630, 'update_timestamp_ms': 1762662805630, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'BQUGNYOPDMOGXW3XUHS5XRMY', 'name': 'projects/ext-datasets/operations/BQUGNYOPDMOGXW3XUHS5XRMY'}\n","Grid 14\n","curr_year 2018\n","Saving data for Barddhaman 2018\n","Task Started {'state': 'READY', 'description': 'Barddhaman_14_2018', 'priority': 100, 'creation_timestamp_ms': 1762662813218, 'update_timestamp_ms': 1762662813218, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'Y3JKMYINEA45AOFFTZIYD6KC', 'name': 'projects/ext-datasets/operations/Y3JKMYINEA45AOFFTZIYD6KC'}\n","Grid 15\n","curr_year 2018\n","Saving data for Barddhaman 2018\n","Task Started {'state': 'READY', 'description': 'Barddhaman_15_2018', 'priority': 100, 'creation_timestamp_ms': 1762662818157, 'update_timestamp_ms': 1762662818157, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'C3ZJVK7IXOW75XKIDHBDRBZD', 'name': 'projects/ext-datasets/operations/C3ZJVK7IXOW75XKIDHBDRBZD'}\n","Grid 16\n","curr_year 2018\n","Saving data for Barddhaman 2018\n","Task Started {'state': 'READY', 'description': 'Barddhaman_16_2018', 'priority': 100, 'creation_timestamp_ms': 1762662826279, 'update_timestamp_ms': 1762662826279, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '3VRJ37SIYS7Q2T4AYGWTA5UO', 'name': 'projects/ext-datasets/operations/3VRJ37SIYS7Q2T4AYGWTA5UO'}\n","Grid 17\n","curr_year 2018\n","Saving data for Barddhaman 2018\n","Task Started {'state': 'READY', 'description': 'Barddhaman_17_2018', 'priority': 100, 'creation_timestamp_ms': 1762662834377, 'update_timestamp_ms': 1762662834377, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'NYI4WE744QRXEN5DLPHQCSOA', 'name': 'projects/ext-datasets/operations/NYI4WE744QRXEN5DLPHQCSOA'}\n","Grid 18\n","curr_year 2018\n","Saving data for Barddhaman 2018\n","Task Started {'state': 'READY', 'description': 'Barddhaman_18_2018', 'priority': 100, 'creation_timestamp_ms': 1762662838462, 'update_timestamp_ms': 1762662838462, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'P73SELSFL5BEZG4LSXWCS34H', 'name': 'projects/ext-datasets/operations/P73SELSFL5BEZG4LSXWCS34H'}\n","Grid 19\n","curr_year 2018\n","Saving data for Barddhaman 2018\n","Task Started {'state': 'READY', 'description': 'Barddhaman_19_2018', 'priority': 100, 'creation_timestamp_ms': 1762662843350, 'update_timestamp_ms': 1762662843350, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'XZKEYHR7WQ7YK3SAZ5NMBTUA', 'name': 'projects/ext-datasets/operations/XZKEYHR7WQ7YK3SAZ5NMBTUA'}\n","Grid 20\n","curr_year 2018\n","Saving data for Barddhaman 2018\n","Task Started {'state': 'READY', 'description': 'Barddhaman_20_2018', 'priority': 100, 'creation_timestamp_ms': 1762662849762, 'update_timestamp_ms': 1762662849762, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'NQ573WM4Z25B2UIRGKI4IANW', 'name': 'projects/ext-datasets/operations/NQ573WM4Z25B2UIRGKI4IANW'}\n","Grid 21\n","curr_year 2018\n","Saving data for Barddhaman 2018\n","Task Started {'state': 'READY', 'description': 'Barddhaman_21_2018', 'priority': 100, 'creation_timestamp_ms': 1762662857674, 'update_timestamp_ms': 1762662857674, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'I5ASBM24ZWGIEIMGWCZWTUI6', 'name': 'projects/ext-datasets/operations/I5ASBM24ZWGIEIMGWCZWTUI6'}\n","Grid 22\n","curr_year 2018\n","Saving data for Barddhaman 2018\n","Task Started {'state': 'READY', 'description': 'Barddhaman_22_2018', 'priority': 100, 'creation_timestamp_ms': 1762662864953, 'update_timestamp_ms': 1762662864953, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'S5CG73D3JKYNCF7KBIL3Q5H4', 'name': 'projects/ext-datasets/operations/S5CG73D3JKYNCF7KBIL3Q5H4'}\n","Grid 23\n","curr_year 2018\n","Saving data for Barddhaman 2018\n","Task Started {'state': 'READY', 'description': 'Barddhaman_23_2018', 'priority': 100, 'creation_timestamp_ms': 1762662871848, 'update_timestamp_ms': 1762662871848, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'W642R3OWEAVV57FVAS3K467P', 'name': 'projects/ext-datasets/operations/W642R3OWEAVV57FVAS3K467P'}\n","Grid 24\n","curr_year 2018\n","Saving data for Barddhaman 2018\n","Task Started {'state': 'READY', 'description': 'Barddhaman_24_2018', 'priority': 100, 'creation_timestamp_ms': 1762662876462, 'update_timestamp_ms': 1762662876462, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'W62H5AE2JSAQTYZNIN7LLOQS', 'name': 'projects/ext-datasets/operations/W62H5AE2JSAQTYZNIN7LLOQS'}\n","Grid 25\n","curr_year 2018\n","Saving data for Barddhaman 2018\n","Task Started {'state': 'READY', 'description': 'Barddhaman_25_2018', 'priority': 100, 'creation_timestamp_ms': 1762662885779, 'update_timestamp_ms': 1762662885779, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'ACYPIOUA4CU4XHTXKEWY5MOG', 'name': 'projects/ext-datasets/operations/ACYPIOUA4CU4XHTXKEWY5MOG'}\n","Grid 26\n","curr_year 2018\n","Saving data for Barddhaman 2018\n","Task Started {'state': 'READY', 'description': 'Barddhaman_26_2018', 'priority': 100, 'creation_timestamp_ms': 1762662893529, 'update_timestamp_ms': 1762662893529, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'OUQPTYB24CYAL7Q6VZHTYC4I', 'name': 'projects/ext-datasets/operations/OUQPTYB24CYAL7Q6VZHTYC4I'}\n","Grid 27\n","curr_year 2018\n","Saving data for Barddhaman 2018\n","Task Started {'state': 'READY', 'description': 'Barddhaman_27_2018', 'priority': 100, 'creation_timestamp_ms': 1762662901313, 'update_timestamp_ms': 1762662901313, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'U5CDX3PVNJRXX7SJQU6FYSPF', 'name': 'projects/ext-datasets/operations/U5CDX3PVNJRXX7SJQU6FYSPF'}\n","Grid 28\n","curr_year 2018\n","Saving data for Barddhaman 2018\n","Task Started {'state': 'READY', 'description': 'Barddhaman_28_2018', 'priority': 100, 'creation_timestamp_ms': 1762662910250, 'update_timestamp_ms': 1762662910250, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'SSA55FB26DNDZYFC6ENMNSGT', 'name': 'projects/ext-datasets/operations/SSA55FB26DNDZYFC6ENMNSGT'}\n","Grid 29\n","curr_year 2018\n","Saving data for Barddhaman 2018\n","Task Started {'state': 'READY', 'description': 'Barddhaman_29_2018', 'priority': 100, 'creation_timestamp_ms': 1762662917244, 'update_timestamp_ms': 1762662917244, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'ASA5WKSDA4IR5ZQUJRH7TLLX', 'name': 'projects/ext-datasets/operations/ASA5WKSDA4IR5ZQUJRH7TLLX'}\n","Grid 30\n","curr_year 2018\n","Saving data for Barddhaman 2018\n","Task Started {'state': 'READY', 'description': 'Barddhaman_30_2018', 'priority': 100, 'creation_timestamp_ms': 1762662921261, 'update_timestamp_ms': 1762662921261, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'HWJKKJLJLX7C2VCCFXM27FR3', 'name': 'projects/ext-datasets/operations/HWJKKJLJLX7C2VCCFXM27FR3'}\n","Grid 31\n","curr_year 2018\n","Saving data for Barddhaman 2018\n","Task Started {'state': 'READY', 'description': 'Barddhaman_31_2018', 'priority': 100, 'creation_timestamp_ms': 1762662929748, 'update_timestamp_ms': 1762662929748, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'Y7QCW5U5SUG2YQRW723RJWAD', 'name': 'projects/ext-datasets/operations/Y7QCW5U5SUG2YQRW723RJWAD'}\n","Grid 32\n","curr_year 2018\n","Saving data for Barddhaman 2018\n","Task Started {'state': 'READY', 'description': 'Barddhaman_32_2018', 'priority': 100, 'creation_timestamp_ms': 1762662935935, 'update_timestamp_ms': 1762662935935, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'CJGX6XIEY3LRQNJFUL3VV2L2', 'name': 'projects/ext-datasets/operations/CJGX6XIEY3LRQNJFUL3VV2L2'}\n","Grid 33\n","curr_year 2018\n","Saving data for Barddhaman 2018\n","Task Started {'state': 'READY', 'description': 'Barddhaman_33_2018', 'priority': 100, 'creation_timestamp_ms': 1762662943825, 'update_timestamp_ms': 1762662943825, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'OPUGTR4BYJQ3ZKPYRJG5PZVQ', 'name': 'projects/ext-datasets/operations/OPUGTR4BYJQ3ZKPYRJG5PZVQ'}\n","Grid 34\n","curr_year 2018\n","Saving data for Barddhaman 2018\n","Task Started {'state': 'READY', 'description': 'Barddhaman_34_2018', 'priority': 100, 'creation_timestamp_ms': 1762662950206, 'update_timestamp_ms': 1762662950206, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'OYRCYIHMQTX7VXUIWWUZIX6K', 'name': 'projects/ext-datasets/operations/OYRCYIHMQTX7VXUIWWUZIX6K'}\n","Grid 35\n","curr_year 2018\n","Saving data for Barddhaman 2018\n","Task Started {'state': 'READY', 'description': 'Barddhaman_35_2018', 'priority': 100, 'creation_timestamp_ms': 1762662958440, 'update_timestamp_ms': 1762662958440, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '6MGJ33DRKPT4Y74IXCKBIB5J', 'name': 'projects/ext-datasets/operations/6MGJ33DRKPT4Y74IXCKBIB5J'}\n","Grid 36\n","curr_year 2018\n","Saving data for Barddhaman 2018\n","Task Started {'state': 'READY', 'description': 'Barddhaman_36_2018', 'priority': 100, 'creation_timestamp_ms': 1762662963435, 'update_timestamp_ms': 1762662963435, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'FCVUFYMLGVUTMYZQBP567VO2', 'name': 'projects/ext-datasets/operations/FCVUFYMLGVUTMYZQBP567VO2'}\n","Grid 37\n","curr_year 2018\n","Saving data for Barddhaman 2018\n","Task Started {'state': 'READY', 'description': 'Barddhaman_37_2018', 'priority': 100, 'creation_timestamp_ms': 1762662968436, 'update_timestamp_ms': 1762662968436, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'E6JZE4FE2W5WUUATWZMRDNTK', 'name': 'projects/ext-datasets/operations/E6JZE4FE2W5WUUATWZMRDNTK'}\n","Grid 38\n","curr_year 2018\n","Saving data for Barddhaman 2018\n","Task Started {'state': 'READY', 'description': 'Barddhaman_38_2018', 'priority': 100, 'creation_timestamp_ms': 1762662973081, 'update_timestamp_ms': 1762662973081, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'YJ67UTGBXBH47VQMEOAT2YAX', 'name': 'projects/ext-datasets/operations/YJ67UTGBXBH47VQMEOAT2YAX'}\n","Grid 39\n","curr_year 2018\n","Saving data for Barddhaman 2018\n","Task Started {'state': 'READY', 'description': 'Barddhaman_39_2018', 'priority': 100, 'creation_timestamp_ms': 1762662980971, 'update_timestamp_ms': 1762662980971, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'BK6DT5O47SHMGFWAB5ANZJ3C', 'name': 'projects/ext-datasets/operations/BK6DT5O47SHMGFWAB5ANZJ3C'}\n","Grid 40\n","curr_year 2018\n","Saving data for Barddhaman 2018\n","Task Started {'state': 'READY', 'description': 'Barddhaman_40_2018', 'priority': 100, 'creation_timestamp_ms': 1762662990232, 'update_timestamp_ms': 1762662990232, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'MA4JSGLARA5IL3IZBMOSAMPQ', 'name': 'projects/ext-datasets/operations/MA4JSGLARA5IL3IZBMOSAMPQ'}\n","Grid 41\n","curr_year 2018\n","Saving data for Barddhaman 2018\n","Task Started {'state': 'READY', 'description': 'Barddhaman_41_2018', 'priority': 100, 'creation_timestamp_ms': 1762662994422, 'update_timestamp_ms': 1762662994422, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'WZXBNEPQ3XMKAAEVULH7H2D2', 'name': 'projects/ext-datasets/operations/WZXBNEPQ3XMKAAEVULH7H2D2'}\n","Grid 42\n","curr_year 2018\n","Saving data for Barddhaman 2018\n","Task Started {'state': 'READY', 'description': 'Barddhaman_42_2018', 'priority': 100, 'creation_timestamp_ms': 1762662998487, 'update_timestamp_ms': 1762662998487, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'SYCOWKQES63QQRNNEAAQSZUN', 'name': 'projects/ext-datasets/operations/SYCOWKQES63QQRNNEAAQSZUN'}\n","Grid 43\n","curr_year 2018\n","Saving data for Barddhaman 2018\n","Task Started {'state': 'READY', 'description': 'Barddhaman_43_2018', 'priority': 100, 'creation_timestamp_ms': 1762663003545, 'update_timestamp_ms': 1762663003545, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'DSVIOMBC7IONT3SLT5I6A7IG', 'name': 'projects/ext-datasets/operations/DSVIOMBC7IONT3SLT5I6A7IG'}\n","Grid 44\n","curr_year 2018\n","Saving data for Barddhaman 2018\n","Task Started {'state': 'READY', 'description': 'Barddhaman_44_2018', 'priority': 100, 'creation_timestamp_ms': 1762663007458, 'update_timestamp_ms': 1762663007458, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'WJ6XRMS4T2PZOJBHRDYZTWUN', 'name': 'projects/ext-datasets/operations/WJ6XRMS4T2PZOJBHRDYZTWUN'}\n","Grid 45\n","curr_year 2018\n","Saving data for Barddhaman 2018\n","Task Started {'state': 'READY', 'description': 'Barddhaman_45_2018', 'priority': 100, 'creation_timestamp_ms': 1762663015024, 'update_timestamp_ms': 1762663015024, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'VUMY6LTNLYIPQIH6HNXOH2DT', 'name': 'projects/ext-datasets/operations/VUMY6LTNLYIPQIH6HNXOH2DT'}\n","Grid 46\n","curr_year 2018\n","Saving data for Barddhaman 2018\n","Task Started {'state': 'READY', 'description': 'Barddhaman_46_2018', 'priority': 100, 'creation_timestamp_ms': 1762663022934, 'update_timestamp_ms': 1762663022934, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '5MYOP6VCGKLOQ2BRLAPTD5VU', 'name': 'projects/ext-datasets/operations/5MYOP6VCGKLOQ2BRLAPTD5VU'}\n","Grid 47\n","curr_year 2018\n","Saving data for Barddhaman 2018\n","Task Started {'state': 'READY', 'description': 'Barddhaman_47_2018', 'priority': 100, 'creation_timestamp_ms': 1762663031299, 'update_timestamp_ms': 1762663031299, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'Z4JZ6IXCRQUSVQT22IYRDRXP', 'name': 'projects/ext-datasets/operations/Z4JZ6IXCRQUSVQT22IYRDRXP'}\n","Grid 48\n","curr_year 2018\n","Saving data for Barddhaman 2018\n","Task Started {'state': 'READY', 'description': 'Barddhaman_48_2018', 'priority': 100, 'creation_timestamp_ms': 1762663041569, 'update_timestamp_ms': 1762663041569, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'H37NQ7RTYBXWI62MU57FCIYK', 'name': 'projects/ext-datasets/operations/H37NQ7RTYBXWI62MU57FCIYK'}\n","Grid 49\n","curr_year 2018\n","Saving data for Barddhaman 2018\n","Task Started {'state': 'READY', 'description': 'Barddhaman_49_2018', 'priority': 100, 'creation_timestamp_ms': 1762663048632, 'update_timestamp_ms': 1762663048632, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'OJQN3MJGBMNGZSLBFCIDKSHN', 'name': 'projects/ext-datasets/operations/OJQN3MJGBMNGZSLBFCIDKSHN'}\n","Grid 50\n","curr_year 2018\n","Saving data for Barddhaman 2018\n","Task Started {'state': 'READY', 'description': 'Barddhaman_50_2018', 'priority': 100, 'creation_timestamp_ms': 1762663056130, 'update_timestamp_ms': 1762663056130, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'ZM72542SRA25SZ7H7YOIUGSR', 'name': 'projects/ext-datasets/operations/ZM72542SRA25SZ7H7YOIUGSR'}\n","Grid 51\n","curr_year 2018\n","Saving data for Barddhaman 2018\n","Task Started {'state': 'READY', 'description': 'Barddhaman_51_2018', 'priority': 100, 'creation_timestamp_ms': 1762663064008, 'update_timestamp_ms': 1762663064008, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'H6PKJHG5OTAASPDE2X3BQXRJ', 'name': 'projects/ext-datasets/operations/H6PKJHG5OTAASPDE2X3BQXRJ'}\n","Grid 52\n","curr_year 2018\n","Saving data for Barddhaman 2018\n","Task Started {'state': 'READY', 'description': 'Barddhaman_52_2018', 'priority': 100, 'creation_timestamp_ms': 1762663073818, 'update_timestamp_ms': 1762663073818, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'HBANIKF2AUIVEE7LN2YJ3HRG', 'name': 'projects/ext-datasets/operations/HBANIKF2AUIVEE7LN2YJ3HRG'}\n","Grid 53\n","curr_year 2018\n","Saving data for Barddhaman 2018\n","Task Started {'state': 'READY', 'description': 'Barddhaman_53_2018', 'priority': 100, 'creation_timestamp_ms': 1762663081691, 'update_timestamp_ms': 1762663081691, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'AB6P57FWLTZ2TDXPKXL656OW', 'name': 'projects/ext-datasets/operations/AB6P57FWLTZ2TDXPKXL656OW'}\n","Grid 54\n","curr_year 2018\n","Saving data for Barddhaman 2018\n","Task Started {'state': 'READY', 'description': 'Barddhaman_54_2018', 'priority': 100, 'creation_timestamp_ms': 1762663089843, 'update_timestamp_ms': 1762663089843, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'YC4KZ5YVVGY3GJSGAEEIU4VQ', 'name': 'projects/ext-datasets/operations/YC4KZ5YVVGY3GJSGAEEIU4VQ'}\n","Grid 55\n","curr_year 2018\n","Saving data for Barddhaman 2018\n","Task Started {'state': 'READY', 'description': 'Barddhaman_55_2018', 'priority': 100, 'creation_timestamp_ms': 1762663096447, 'update_timestamp_ms': 1762663096447, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'RYBNFECFMNKJLSVQH5KKG26K', 'name': 'projects/ext-datasets/operations/RYBNFECFMNKJLSVQH5KKG26K'}\n","Grid 56\n","curr_year 2018\n","Saving data for Barddhaman 2018\n","Task Started {'state': 'READY', 'description': 'Barddhaman_56_2018', 'priority': 100, 'creation_timestamp_ms': 1762663108432, 'update_timestamp_ms': 1762663108432, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '5AKLGISYOJY536SSIE4J5XPV', 'name': 'projects/ext-datasets/operations/5AKLGISYOJY536SSIE4J5XPV'}\n","Grid 57\n","curr_year 2018\n","Saving data for Barddhaman 2018\n","Task Started {'state': 'READY', 'description': 'Barddhaman_57_2018', 'priority': 100, 'creation_timestamp_ms': 1762663116314, 'update_timestamp_ms': 1762663116314, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '65FXQIDVL7AAKPCQSXDIJVWL', 'name': 'projects/ext-datasets/operations/65FXQIDVL7AAKPCQSXDIJVWL'}\n","Grid 58\n","curr_year 2018\n","Saving data for Barddhaman 2018\n","Task Started {'state': 'READY', 'description': 'Barddhaman_58_2018', 'priority': 100, 'creation_timestamp_ms': 1762663122438, 'update_timestamp_ms': 1762663122438, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'YBDAFJBLFXJTACPQYMGOQBMK', 'name': 'projects/ext-datasets/operations/YBDAFJBLFXJTACPQYMGOQBMK'}\n","Grid 59\n","curr_year 2018\n","Saving data for Barddhaman 2018\n","Task Started {'state': 'READY', 'description': 'Barddhaman_59_2018', 'priority': 100, 'creation_timestamp_ms': 1762663131585, 'update_timestamp_ms': 1762663131585, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'AB6LY3PVHGZ747TMIB4YXZQ7', 'name': 'projects/ext-datasets/operations/AB6LY3PVHGZ747TMIB4YXZQ7'}\n","Grid 60\n","curr_year 2018\n","Saving data for Barddhaman 2018\n","Task Started {'state': 'READY', 'description': 'Barddhaman_60_2018', 'priority': 100, 'creation_timestamp_ms': 1762663135551, 'update_timestamp_ms': 1762663135551, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '5JDMOFKKXGHEK7LEIC77LH5A', 'name': 'projects/ext-datasets/operations/5JDMOFKKXGHEK7LEIC77LH5A'}\n","Grid 61\n","curr_year 2018\n","Saving data for Barddhaman 2018\n","Task Started {'state': 'READY', 'description': 'Barddhaman_61_2018', 'priority': 100, 'creation_timestamp_ms': 1762663146649, 'update_timestamp_ms': 1762663146649, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'AZEP66S23AF6WRDZ4LBK36TC', 'name': 'projects/ext-datasets/operations/AZEP66S23AF6WRDZ4LBK36TC'}\n","Grid 62\n","curr_year 2018\n","Saving data for Barddhaman 2018\n","Task Started {'state': 'READY', 'description': 'Barddhaman_62_2018', 'priority': 100, 'creation_timestamp_ms': 1762663153661, 'update_timestamp_ms': 1762663153661, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'VTRIUL6HUXUIV535Z3ECYNG4', 'name': 'projects/ext-datasets/operations/VTRIUL6HUXUIV535Z3ECYNG4'}\n","Grid 63\n","curr_year 2018\n","Saving data for Barddhaman 2018\n","Task Started {'state': 'READY', 'description': 'Barddhaman_63_2018', 'priority': 100, 'creation_timestamp_ms': 1762663160000, 'update_timestamp_ms': 1762663160000, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'AEC6PK6LKUXF5JF6ENX375BT', 'name': 'projects/ext-datasets/operations/AEC6PK6LKUXF5JF6ENX375BT'}\n","Grid 64\n","curr_year 2018\n","Saving data for Barddhaman 2018\n","Task Started {'state': 'READY', 'description': 'Barddhaman_64_2018', 'priority': 100, 'creation_timestamp_ms': 1762663166893, 'update_timestamp_ms': 1762663166893, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'AJGFGVSX5IGYBQ3F47E6PG7P', 'name': 'projects/ext-datasets/operations/AJGFGVSX5IGYBQ3F47E6PG7P'}\n","Grid 65\n","curr_year 2018\n","Saving data for Barddhaman 2018\n","Task Started {'state': 'READY', 'description': 'Barddhaman_65_2018', 'priority': 100, 'creation_timestamp_ms': 1762663173953, 'update_timestamp_ms': 1762663173953, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'DCZ3C5BFZU4OJXKNOW6DAMRL', 'name': 'projects/ext-datasets/operations/DCZ3C5BFZU4OJXKNOW6DAMRL'}\n","Grid 66\n","curr_year 2018\n","Saving data for Barddhaman 2018\n","Task Started {'state': 'READY', 'description': 'Barddhaman_66_2018', 'priority': 100, 'creation_timestamp_ms': 1762663182967, 'update_timestamp_ms': 1762663182967, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'XLGDIO5DJPDONI2WYGYF3BLU', 'name': 'projects/ext-datasets/operations/XLGDIO5DJPDONI2WYGYF3BLU'}\n","Grid 67\n","curr_year 2018\n","Saving data for Barddhaman 2018\n","Task Started {'state': 'READY', 'description': 'Barddhaman_67_2018', 'priority': 100, 'creation_timestamp_ms': 1762663190118, 'update_timestamp_ms': 1762663190118, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'XYFQNFSPOFRECNPSHKFO5QAA', 'name': 'projects/ext-datasets/operations/XYFQNFSPOFRECNPSHKFO5QAA'}\n","Grid 68\n","curr_year 2018\n","Saving data for Barddhaman 2018\n","Task Started {'state': 'READY', 'description': 'Barddhaman_68_2018', 'priority': 100, 'creation_timestamp_ms': 1762663194061, 'update_timestamp_ms': 1762663194061, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'WA7YEKTAQQLLTSRWAF7XRFUG', 'name': 'projects/ext-datasets/operations/WA7YEKTAQQLLTSRWAF7XRFUG'}\n","Grid 69\n","curr_year 2018\n","Saving data for Barddhaman 2018\n","Task Started {'state': 'READY', 'description': 'Barddhaman_69_2018', 'priority': 100, 'creation_timestamp_ms': 1762663201149, 'update_timestamp_ms': 1762663201149, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'VTZHVVRZY6I3DSFZ4ETCDI3A', 'name': 'projects/ext-datasets/operations/VTZHVVRZY6I3DSFZ4ETCDI3A'}\n","Grid 70\n","curr_year 2018\n","Saving data for Barddhaman 2018\n","Task Started {'state': 'READY', 'description': 'Barddhaman_70_2018', 'priority': 100, 'creation_timestamp_ms': 1762663206455, 'update_timestamp_ms': 1762663206455, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'RC4A3BRD7HKBCMHNTWDM7KIX', 'name': 'projects/ext-datasets/operations/RC4A3BRD7HKBCMHNTWDM7KIX'}\n","Grid 71\n","curr_year 2018\n","Saving data for Barddhaman 2018\n","Task Started {'state': 'READY', 'description': 'Barddhaman_71_2018', 'priority': 100, 'creation_timestamp_ms': 1762663215535, 'update_timestamp_ms': 1762663215535, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'WJVSAIOQZCFK7GYNGOOJZTPT', 'name': 'projects/ext-datasets/operations/WJVSAIOQZCFK7GYNGOOJZTPT'}\n","Grid 72\n","curr_year 2018\n","Saving data for Barddhaman 2018\n","Task Started {'state': 'READY', 'description': 'Barddhaman_72_2018', 'priority': 100, 'creation_timestamp_ms': 1762663222778, 'update_timestamp_ms': 1762663222778, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'NV7WT3N7XF7IOBORBI3VFM2K', 'name': 'projects/ext-datasets/operations/NV7WT3N7XF7IOBORBI3VFM2K'}\n","Grid 73\n","curr_year 2018\n","Saving data for Barddhaman 2018\n","Task Started {'state': 'READY', 'description': 'Barddhaman_73_2018', 'priority': 100, 'creation_timestamp_ms': 1762663227215, 'update_timestamp_ms': 1762663227215, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '6GA356K45UAKYMSM6T37BUF7', 'name': 'projects/ext-datasets/operations/6GA356K45UAKYMSM6T37BUF7'}\n","Grid 74\n","curr_year 2018\n","Saving data for Barddhaman 2018\n","Task Started {'state': 'READY', 'description': 'Barddhaman_74_2018', 'priority': 100, 'creation_timestamp_ms': 1762663234448, 'update_timestamp_ms': 1762663234448, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'OLWSBKKVMBWHQ64BNHTYWUM4', 'name': 'projects/ext-datasets/operations/OLWSBKKVMBWHQ64BNHTYWUM4'}\n","Grid 75\n","curr_year 2018\n","Saving data for Barddhaman 2018\n","Task Started {'state': 'READY', 'description': 'Barddhaman_75_2018', 'priority': 100, 'creation_timestamp_ms': 1762663244824, 'update_timestamp_ms': 1762663244824, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'RUBCPN5V42II5GI53VMEOHWG', 'name': 'projects/ext-datasets/operations/RUBCPN5V42II5GI53VMEOHWG'}\n","Grid 76\n","curr_year 2018\n","Saving data for Barddhaman 2018\n","Task Started {'state': 'READY', 'description': 'Barddhaman_76_2018', 'priority': 100, 'creation_timestamp_ms': 1762663250937, 'update_timestamp_ms': 1762663250937, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'HE2V5OOESE3NE7ABPGABA2XR', 'name': 'projects/ext-datasets/operations/HE2V5OOESE3NE7ABPGABA2XR'}\n","Grid 77\n","curr_year 2018\n","Saving data for Barddhaman 2018\n","Task Started {'state': 'READY', 'description': 'Barddhaman_77_2018', 'priority': 100, 'creation_timestamp_ms': 1762663259804, 'update_timestamp_ms': 1762663259804, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'L3DC3VJTRX36H3TCSQX4PYUZ', 'name': 'projects/ext-datasets/operations/L3DC3VJTRX36H3TCSQX4PYUZ'}\n","Grid 78\n","curr_year 2018\n","Saving data for Barddhaman 2018\n","Task Started {'state': 'READY', 'description': 'Barddhaman_78_2018', 'priority': 100, 'creation_timestamp_ms': 1762663267880, 'update_timestamp_ms': 1762663267880, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'ULONDXUUNB5HF2LA777GPOHE', 'name': 'projects/ext-datasets/operations/ULONDXUUNB5HF2LA777GPOHE'}\n","Grid 79\n","curr_year 2018\n","Saving data for Barddhaman 2018\n","Task Started {'state': 'READY', 'description': 'Barddhaman_79_2018', 'priority': 100, 'creation_timestamp_ms': 1762663275114, 'update_timestamp_ms': 1762663275114, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'VA5VHD2OWYAHA43AE4UH34WB', 'name': 'projects/ext-datasets/operations/VA5VHD2OWYAHA43AE4UH34WB'}\n","Grid 80\n","curr_year 2018\n","Saving data for Barddhaman 2018\n","Task Started {'state': 'READY', 'description': 'Barddhaman_80_2018', 'priority': 100, 'creation_timestamp_ms': 1762663282179, 'update_timestamp_ms': 1762663282179, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '5I5RISHZ67Y5E74MDD5PBTSX', 'name': 'projects/ext-datasets/operations/5I5RISHZ67Y5E74MDD5PBTSX'}\n","Grid 81\n","curr_year 2018\n","Saving data for Barddhaman 2018\n","Task Started {'state': 'READY', 'description': 'Barddhaman_81_2018', 'priority': 100, 'creation_timestamp_ms': 1762663288154, 'update_timestamp_ms': 1762663288154, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'T4HS6R3X6PEJSRUF53ZXJCFP', 'name': 'projects/ext-datasets/operations/T4HS6R3X6PEJSRUF53ZXJCFP'}\n","Grid 82\n","curr_year 2018\n","Saving data for Barddhaman 2018\n","Task Started {'state': 'READY', 'description': 'Barddhaman_82_2018', 'priority': 100, 'creation_timestamp_ms': 1762663295485, 'update_timestamp_ms': 1762663295485, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'A5PNFUVXZGFQM4BRSEDSFXXZ', 'name': 'projects/ext-datasets/operations/A5PNFUVXZGFQM4BRSEDSFXXZ'}\n","Grid 83\n","curr_year 2018\n","Saving data for Barddhaman 2018\n","Task Started {'state': 'READY', 'description': 'Barddhaman_83_2018', 'priority': 100, 'creation_timestamp_ms': 1762663302995, 'update_timestamp_ms': 1762663302995, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '2A3MCB6W3ITEZZPSWLPDGVHL', 'name': 'projects/ext-datasets/operations/2A3MCB6W3ITEZZPSWLPDGVHL'}\n","Grid 84\n","curr_year 2018\n","Saving data for Barddhaman 2018\n","Task Started {'state': 'READY', 'description': 'Barddhaman_84_2018', 'priority': 100, 'creation_timestamp_ms': 1762663311352, 'update_timestamp_ms': 1762663311352, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'KLFS36SLEME2NACN5TYIRV2O', 'name': 'projects/ext-datasets/operations/KLFS36SLEME2NACN5TYIRV2O'}\n","Grid 85\n","curr_year 2018\n","Saving data for Barddhaman 2018\n","Task Started {'state': 'READY', 'description': 'Barddhaman_85_2018', 'priority': 100, 'creation_timestamp_ms': 1762663320281, 'update_timestamp_ms': 1762663320281, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '635EVSMRKTJNVKVMSCWHZRI5', 'name': 'projects/ext-datasets/operations/635EVSMRKTJNVKVMSCWHZRI5'}\n","Grid 86\n","curr_year 2018\n","Saving data for Barddhaman 2018\n","Task Started {'state': 'READY', 'description': 'Barddhaman_86_2018', 'priority': 100, 'creation_timestamp_ms': 1762663327089, 'update_timestamp_ms': 1762663327089, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '2KJYEKDKZLN7DT3FHHQF5Q7T', 'name': 'projects/ext-datasets/operations/2KJYEKDKZLN7DT3FHHQF5Q7T'}\n","Grid 87\n","curr_year 2018\n","Saving data for Barddhaman 2018\n","Task Started {'state': 'READY', 'description': 'Barddhaman_87_2018', 'priority': 100, 'creation_timestamp_ms': 1762663335646, 'update_timestamp_ms': 1762663335646, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'HYMXNPZCURLDUHXHMZW4IRFB', 'name': 'projects/ext-datasets/operations/HYMXNPZCURLDUHXHMZW4IRFB'}\n","Grid 88\n","curr_year 2018\n","Saving data for Barddhaman 2018\n","Task Started {'state': 'READY', 'description': 'Barddhaman_88_2018', 'priority': 100, 'creation_timestamp_ms': 1762663340319, 'update_timestamp_ms': 1762663340319, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'YJTO5G3CDCRCHQGAJ33TFNHL', 'name': 'projects/ext-datasets/operations/YJTO5G3CDCRCHQGAJ33TFNHL'}\n","Grid 89\n","curr_year 2018\n","Saving data for Barddhaman 2018\n","Task Started {'state': 'READY', 'description': 'Barddhaman_89_2018', 'priority': 100, 'creation_timestamp_ms': 1762663348259, 'update_timestamp_ms': 1762663348259, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'DR4LZPVI52LQAZBSBKEMX6CF', 'name': 'projects/ext-datasets/operations/DR4LZPVI52LQAZBSBKEMX6CF'}\n","Grid 90\n","curr_year 2018\n","Saving data for Barddhaman 2018\n","Task Started {'state': 'READY', 'description': 'Barddhaman_90_2018', 'priority': 100, 'creation_timestamp_ms': 1762663355018, 'update_timestamp_ms': 1762663355018, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'JYNU56WQSWHVJCXNU4Q3YSBN', 'name': 'projects/ext-datasets/operations/JYNU56WQSWHVJCXNU4Q3YSBN'}\n","Grid 91\n","curr_year 2018\n","Saving data for Barddhaman 2018\n","Task Started {'state': 'READY', 'description': 'Barddhaman_91_2018', 'priority': 100, 'creation_timestamp_ms': 1762663361507, 'update_timestamp_ms': 1762663361507, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'NYOFRCDWKY6XN57GICRHY4YB', 'name': 'projects/ext-datasets/operations/NYOFRCDWKY6XN57GICRHY4YB'}\n","Grid 92\n","curr_year 2018\n","Saving data for Barddhaman 2018\n","Task Started {'state': 'READY', 'description': 'Barddhaman_92_2018', 'priority': 100, 'creation_timestamp_ms': 1762663369331, 'update_timestamp_ms': 1762663369331, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '6DXI2S35UHUF474BBJOGMKR3', 'name': 'projects/ext-datasets/operations/6DXI2S35UHUF474BBJOGMKR3'}\n","Grid 93\n","curr_year 2018\n","Saving data for Barddhaman 2018\n","Task Started {'state': 'READY', 'description': 'Barddhaman_93_2018', 'priority': 100, 'creation_timestamp_ms': 1762663375915, 'update_timestamp_ms': 1762663375915, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'C4E2A3T3VFQBMPWKVZS5XKUA', 'name': 'projects/ext-datasets/operations/C4E2A3T3VFQBMPWKVZS5XKUA'}\n","Grid 94\n","curr_year 2018\n","Saving data for Barddhaman 2018\n","Task Started {'state': 'READY', 'description': 'Barddhaman_94_2018', 'priority': 100, 'creation_timestamp_ms': 1762663382522, 'update_timestamp_ms': 1762663382522, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'QKYZUU5OH3B7ODQEERSJ577G', 'name': 'projects/ext-datasets/operations/QKYZUU5OH3B7ODQEERSJ577G'}\n","Grid 95\n","curr_year 2018\n","Saving data for Barddhaman 2018\n","Task Started {'state': 'READY', 'description': 'Barddhaman_95_2018', 'priority': 100, 'creation_timestamp_ms': 1762663391225, 'update_timestamp_ms': 1762663391225, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'MF2FN325BEDATDQ3Q36ALVWJ', 'name': 'projects/ext-datasets/operations/MF2FN325BEDATDQ3Q36ALVWJ'}\n","Grid 96\n","curr_year 2018\n","Saving data for Barddhaman 2018\n","Task Started {'state': 'READY', 'description': 'Barddhaman_96_2018', 'priority': 100, 'creation_timestamp_ms': 1762663400147, 'update_timestamp_ms': 1762663400147, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'S5T4MFZXEOIYBUX2UNZ6VCKA', 'name': 'projects/ext-datasets/operations/S5T4MFZXEOIYBUX2UNZ6VCKA'}\n","Grid 97\n","curr_year 2018\n","Saving data for Barddhaman 2018\n","Task Started {'state': 'READY', 'description': 'Barddhaman_97_2018', 'priority': 100, 'creation_timestamp_ms': 1762663407099, 'update_timestamp_ms': 1762663407099, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'UARCOETQB7CQ4PVNKAIPLJDN', 'name': 'projects/ext-datasets/operations/UARCOETQB7CQ4PVNKAIPLJDN'}\n","Grid 98\n","curr_year 2018\n","Saving data for Barddhaman 2018\n","Task Started {'state': 'READY', 'description': 'Barddhaman_98_2018', 'priority': 100, 'creation_timestamp_ms': 1762663415117, 'update_timestamp_ms': 1762663415117, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'I7UQA5G2IBIOLNAJSDMI5QH7', 'name': 'projects/ext-datasets/operations/I7UQA5G2IBIOLNAJSDMI5QH7'}\n","Grid 99\n","curr_year 2018\n","Saving data for Barddhaman 2018\n","Task Started {'state': 'READY', 'description': 'Barddhaman_99_2018', 'priority': 100, 'creation_timestamp_ms': 1762663418758, 'update_timestamp_ms': 1762663418758, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'EOJAWWDCL556YQVZTBZXZSB7', 'name': 'projects/ext-datasets/operations/EOJAWWDCL556YQVZTBZXZSB7'}\n","Grid 100\n","curr_year 2018\n","Saving data for Barddhaman 2018\n","Task Started {'state': 'READY', 'description': 'Barddhaman_100_2018', 'priority': 100, 'creation_timestamp_ms': 1762663422228, 'update_timestamp_ms': 1762663422228, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'MGULUVTSCMVEGLIRYY5MGGZX', 'name': 'projects/ext-datasets/operations/MGULUVTSCMVEGLIRYY5MGGZX'}\n","Grid 101\n","curr_year 2018\n","Saving data for Barddhaman 2018\n","Task Started {'state': 'READY', 'description': 'Barddhaman_101_2018', 'priority': 100, 'creation_timestamp_ms': 1762663429666, 'update_timestamp_ms': 1762663429666, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'QGGGEUAVN5QJWIM3KNVEBROD', 'name': 'projects/ext-datasets/operations/QGGGEUAVN5QJWIM3KNVEBROD'}\n","Grid 102\n","curr_year 2018\n","Saving data for Barddhaman 2018\n","Task Started {'state': 'READY', 'description': 'Barddhaman_102_2018', 'priority': 100, 'creation_timestamp_ms': 1762663435992, 'update_timestamp_ms': 1762663435992, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'IRKN3Q2SUO57GS6GBRKUHUAR', 'name': 'projects/ext-datasets/operations/IRKN3Q2SUO57GS6GBRKUHUAR'}\n","Grid 103\n","curr_year 2018\n","Saving data for Barddhaman 2018\n","Task Started {'state': 'READY', 'description': 'Barddhaman_103_2018', 'priority': 100, 'creation_timestamp_ms': 1762663443638, 'update_timestamp_ms': 1762663443638, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '6N5C7M3TD73HF72B4TSEDWRP', 'name': 'projects/ext-datasets/operations/6N5C7M3TD73HF72B4TSEDWRP'}\n","Grid 104\n","curr_year 2018\n","Saving data for Barddhaman 2018\n","Task Started {'state': 'READY', 'description': 'Barddhaman_104_2018', 'priority': 100, 'creation_timestamp_ms': 1762663448017, 'update_timestamp_ms': 1762663448017, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'LX4OUSI7VBMLE7MEW5ABRB5G', 'name': 'projects/ext-datasets/operations/LX4OUSI7VBMLE7MEW5ABRB5G'}\n","Grid 105\n","curr_year 2018\n","Saving data for Barddhaman 2018\n","Task Started {'state': 'READY', 'description': 'Barddhaman_105_2018', 'priority': 100, 'creation_timestamp_ms': 1762663455659, 'update_timestamp_ms': 1762663455659, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '4R4HCC4REODQHC7PIRJ6OJSW', 'name': 'projects/ext-datasets/operations/4R4HCC4REODQHC7PIRJ6OJSW'}\n","Grid 106\n","curr_year 2018\n","Saving data for Barddhaman 2018\n","Task Started {'state': 'READY', 'description': 'Barddhaman_106_2018', 'priority': 100, 'creation_timestamp_ms': 1762663464144, 'update_timestamp_ms': 1762663464144, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'JLKJ7YXCPRAIQXOTVQFQDHGL', 'name': 'projects/ext-datasets/operations/JLKJ7YXCPRAIQXOTVQFQDHGL'}\n","Grid 107\n","curr_year 2018\n","Saving data for Barddhaman 2018\n","Task Started {'state': 'READY', 'description': 'Barddhaman_107_2018', 'priority': 100, 'creation_timestamp_ms': 1762663471980, 'update_timestamp_ms': 1762663471980, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'CPXTOZCKCJQPL22BOY4HJKB2', 'name': 'projects/ext-datasets/operations/CPXTOZCKCJQPL22BOY4HJKB2'}\n","Grid 108\n","curr_year 2018\n","Saving data for Barddhaman 2018\n","Task Started {'state': 'READY', 'description': 'Barddhaman_108_2018', 'priority': 100, 'creation_timestamp_ms': 1762663480112, 'update_timestamp_ms': 1762663480112, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '7WQUBZI63PAYLPWRN7TI7DUO', 'name': 'projects/ext-datasets/operations/7WQUBZI63PAYLPWRN7TI7DUO'}\n","Grid 109\n","curr_year 2018\n","Saving data for Barddhaman 2018\n","Task Started {'state': 'READY', 'description': 'Barddhaman_109_2018', 'priority': 100, 'creation_timestamp_ms': 1762663484678, 'update_timestamp_ms': 1762663484678, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '26277CAVBGAF6SO3IDWCUQ23', 'name': 'projects/ext-datasets/operations/26277CAVBGAF6SO3IDWCUQ23'}\n","Grid 110\n","curr_year 2018\n","Saving data for Barddhaman 2018\n","Task Started {'state': 'READY', 'description': 'Barddhaman_110_2018', 'priority': 100, 'creation_timestamp_ms': 1762663493443, 'update_timestamp_ms': 1762663493443, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'GUVFSMFTRMKQQ66SYTHYTY7M', 'name': 'projects/ext-datasets/operations/GUVFSMFTRMKQQ66SYTHYTY7M'}\n","Grid 111\n","curr_year 2018\n","Saving data for Barddhaman 2018\n","Task Started {'state': 'READY', 'description': 'Barddhaman_111_2018', 'priority': 100, 'creation_timestamp_ms': 1762663501199, 'update_timestamp_ms': 1762663501199, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'BPIT2V4T7LJAIQ7SHCXRH623', 'name': 'projects/ext-datasets/operations/BPIT2V4T7LJAIQ7SHCXRH623'}\n","2263.010017156601\n","Year 2018, District 13: Birbhum, grids: 76\n","Grid 0\n","curr_year 2018\n","Saving data for Birbhum 2018\n","Task Started {'state': 'READY', 'description': 'Birbhum_0_2018', 'priority': 100, 'creation_timestamp_ms': 1762663511593, 'update_timestamp_ms': 1762663511593, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'H2JM3MGIBTGUZZ6QWRTHS5WC', 'name': 'projects/ext-datasets/operations/H2JM3MGIBTGUZZ6QWRTHS5WC'}\n","Grid 1\n","curr_year 2018\n","Saving data for Birbhum 2018\n","Task Started {'state': 'READY', 'description': 'Birbhum_1_2018', 'priority': 100, 'creation_timestamp_ms': 1762663517428, 'update_timestamp_ms': 1762663517428, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'OM7PMNB6W5GCYJSLARMRBMMH', 'name': 'projects/ext-datasets/operations/OM7PMNB6W5GCYJSLARMRBMMH'}\n","Grid 2\n","curr_year 2018\n","Saving data for Birbhum 2018\n","Task Started {'state': 'READY', 'description': 'Birbhum_2_2018', 'priority': 100, 'creation_timestamp_ms': 1762663523539, 'update_timestamp_ms': 1762663523539, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'YKL6TAP2ZX3RUDTEFRAE72SP', 'name': 'projects/ext-datasets/operations/YKL6TAP2ZX3RUDTEFRAE72SP'}\n","Grid 3\n","curr_year 2018\n","Saving data for Birbhum 2018\n","Task Started {'state': 'READY', 'description': 'Birbhum_3_2018', 'priority': 100, 'creation_timestamp_ms': 1762663528193, 'update_timestamp_ms': 1762663528193, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'CYAWF4ZRFEB3FQYH2XQDDLVP', 'name': 'projects/ext-datasets/operations/CYAWF4ZRFEB3FQYH2XQDDLVP'}\n","Grid 4\n","curr_year 2018\n","Saving data for Birbhum 2018\n","Task Started {'state': 'READY', 'description': 'Birbhum_4_2018', 'priority': 100, 'creation_timestamp_ms': 1762663533339, 'update_timestamp_ms': 1762663533339, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'H3DJUDHAMRAA6ZC2WJS2LITN', 'name': 'projects/ext-datasets/operations/H3DJUDHAMRAA6ZC2WJS2LITN'}\n","Grid 5\n","curr_year 2018\n","Saving data for Birbhum 2018\n","Task Started {'state': 'READY', 'description': 'Birbhum_5_2018', 'priority': 100, 'creation_timestamp_ms': 1762663537707, 'update_timestamp_ms': 1762663537707, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '4CYTSCWBPT7UV557XNCHSE6D', 'name': 'projects/ext-datasets/operations/4CYTSCWBPT7UV557XNCHSE6D'}\n","Grid 6\n","curr_year 2018\n","Saving data for Birbhum 2018\n","Task Started {'state': 'READY', 'description': 'Birbhum_6_2018', 'priority': 100, 'creation_timestamp_ms': 1762663541366, 'update_timestamp_ms': 1762663541366, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'JADB7APCIEYRKWLKQTDD3XDB', 'name': 'projects/ext-datasets/operations/JADB7APCIEYRKWLKQTDD3XDB'}\n","Grid 7\n","curr_year 2018\n","Saving data for Birbhum 2018\n","Task Started {'state': 'READY', 'description': 'Birbhum_7_2018', 'priority': 100, 'creation_timestamp_ms': 1762663548264, 'update_timestamp_ms': 1762663548264, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'LIR5JNBB3JRRYRRGHSSILNLM', 'name': 'projects/ext-datasets/operations/LIR5JNBB3JRRYRRGHSSILNLM'}\n","Grid 8\n","curr_year 2018\n","Saving data for Birbhum 2018\n","Task Started {'state': 'READY', 'description': 'Birbhum_8_2018', 'priority': 100, 'creation_timestamp_ms': 1762663554849, 'update_timestamp_ms': 1762663554849, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'ZQMJOQSKYN46CWTGYVECLQP2', 'name': 'projects/ext-datasets/operations/ZQMJOQSKYN46CWTGYVECLQP2'}\n","Grid 9\n","curr_year 2018\n","Saving data for Birbhum 2018\n","Task Started {'state': 'READY', 'description': 'Birbhum_9_2018', 'priority': 100, 'creation_timestamp_ms': 1762663560776, 'update_timestamp_ms': 1762663560776, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'QUAJVKJAADMX5B64YMBYMCCT', 'name': 'projects/ext-datasets/operations/QUAJVKJAADMX5B64YMBYMCCT'}\n","Grid 10\n","curr_year 2018\n","Saving data for Birbhum 2018\n","Task Started {'state': 'READY', 'description': 'Birbhum_10_2018', 'priority': 100, 'creation_timestamp_ms': 1762663564605, 'update_timestamp_ms': 1762663564605, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'ZGQMYXQBNSUCG7JOQNNLGMHQ', 'name': 'projects/ext-datasets/operations/ZGQMYXQBNSUCG7JOQNNLGMHQ'}\n","Grid 11\n","curr_year 2018\n","Saving data for Birbhum 2018\n","Task Started {'state': 'READY', 'description': 'Birbhum_11_2018', 'priority': 100, 'creation_timestamp_ms': 1762663574206, 'update_timestamp_ms': 1762663574206, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'BOHTBJI7X4RBKWCTAH2SGO42', 'name': 'projects/ext-datasets/operations/BOHTBJI7X4RBKWCTAH2SGO42'}\n","Grid 12\n","curr_year 2018\n","Saving data for Birbhum 2018\n","Task Started {'state': 'READY', 'description': 'Birbhum_12_2018', 'priority': 100, 'creation_timestamp_ms': 1762663578321, 'update_timestamp_ms': 1762663578321, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'QOIF6NPM4NX7WXYVFTOD62HI', 'name': 'projects/ext-datasets/operations/QOIF6NPM4NX7WXYVFTOD62HI'}\n","Grid 13\n","curr_year 2018\n","Saving data for Birbhum 2018\n","Task Started {'state': 'READY', 'description': 'Birbhum_13_2018', 'priority': 100, 'creation_timestamp_ms': 1762663585003, 'update_timestamp_ms': 1762663585003, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'FEWZDVACV3MM73VU56O6QKCR', 'name': 'projects/ext-datasets/operations/FEWZDVACV3MM73VU56O6QKCR'}\n","Grid 14\n","curr_year 2018\n","Saving data for Birbhum 2018\n","Task Started {'state': 'READY', 'description': 'Birbhum_14_2018', 'priority': 100, 'creation_timestamp_ms': 1762663592253, 'update_timestamp_ms': 1762663592253, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'AEGN7KBLVW7TDCGDJZE3UPRK', 'name': 'projects/ext-datasets/operations/AEGN7KBLVW7TDCGDJZE3UPRK'}\n","Grid 15\n","curr_year 2018\n","Saving data for Birbhum 2018\n","Task Started {'state': 'READY', 'description': 'Birbhum_15_2018', 'priority': 100, 'creation_timestamp_ms': 1762663600087, 'update_timestamp_ms': 1762663600087, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '6A35UOECIKO7IDU7VASDOPA2', 'name': 'projects/ext-datasets/operations/6A35UOECIKO7IDU7VASDOPA2'}\n","Grid 16\n","curr_year 2018\n","Saving data for Birbhum 2018\n","Task Started {'state': 'READY', 'description': 'Birbhum_16_2018', 'priority': 100, 'creation_timestamp_ms': 1762663607741, 'update_timestamp_ms': 1762663607741, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'QUMNIBMBMU77XUHLLJNEWZLO', 'name': 'projects/ext-datasets/operations/QUMNIBMBMU77XUHLLJNEWZLO'}\n","Grid 17\n","curr_year 2018\n","Saving data for Birbhum 2018\n","Task Started {'state': 'READY', 'description': 'Birbhum_17_2018', 'priority': 100, 'creation_timestamp_ms': 1762663615561, 'update_timestamp_ms': 1762663615561, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'GCARZBAT5AWWRPH3OJW4BSVN', 'name': 'projects/ext-datasets/operations/GCARZBAT5AWWRPH3OJW4BSVN'}\n","Grid 18\n","curr_year 2018\n","Saving data for Birbhum 2018\n","Task Started {'state': 'READY', 'description': 'Birbhum_18_2018', 'priority': 100, 'creation_timestamp_ms': 1762663625473, 'update_timestamp_ms': 1762663625473, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'WXXV6TUVDZRCNLGCDAIRSLTY', 'name': 'projects/ext-datasets/operations/WXXV6TUVDZRCNLGCDAIRSLTY'}\n","Grid 19\n","curr_year 2018\n","Saving data for Birbhum 2018\n","Task Started {'state': 'READY', 'description': 'Birbhum_19_2018', 'priority': 100, 'creation_timestamp_ms': 1762663633418, 'update_timestamp_ms': 1762663633418, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'NOOPAHRKEMANFDATGNQPBLF5', 'name': 'projects/ext-datasets/operations/NOOPAHRKEMANFDATGNQPBLF5'}\n","Grid 20\n","curr_year 2018\n","Saving data for Birbhum 2018\n","Task Started {'state': 'READY', 'description': 'Birbhum_20_2018', 'priority': 100, 'creation_timestamp_ms': 1762663640734, 'update_timestamp_ms': 1762663640734, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'JC2KD7HRDGJJ4NGFRJ456FPF', 'name': 'projects/ext-datasets/operations/JC2KD7HRDGJJ4NGFRJ456FPF'}\n","Grid 21\n","curr_year 2018\n","Saving data for Birbhum 2018\n","Task Started {'state': 'READY', 'description': 'Birbhum_21_2018', 'priority': 100, 'creation_timestamp_ms': 1762663646211, 'update_timestamp_ms': 1762663646211, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'QTLTMLASNBBJ3K6P5N5ZLAMG', 'name': 'projects/ext-datasets/operations/QTLTMLASNBBJ3K6P5N5ZLAMG'}\n","Grid 22\n","curr_year 2018\n","Saving data for Birbhum 2018\n","Task Started {'state': 'READY', 'description': 'Birbhum_22_2018', 'priority': 100, 'creation_timestamp_ms': 1762663650001, 'update_timestamp_ms': 1762663650001, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'BMUV7BHDMUGYIR2DNNMAOXVY', 'name': 'projects/ext-datasets/operations/BMUV7BHDMUGYIR2DNNMAOXVY'}\n","Grid 23\n","curr_year 2018\n","Saving data for Birbhum 2018\n","Task Started {'state': 'READY', 'description': 'Birbhum_23_2018', 'priority': 100, 'creation_timestamp_ms': 1762663658530, 'update_timestamp_ms': 1762663658530, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'G5Q323CK5XIMMXQ7R32EOCOY', 'name': 'projects/ext-datasets/operations/G5Q323CK5XIMMXQ7R32EOCOY'}\n","Grid 24\n","curr_year 2018\n","Saving data for Birbhum 2018\n","Task Started {'state': 'READY', 'description': 'Birbhum_24_2018', 'priority': 100, 'creation_timestamp_ms': 1762663665453, 'update_timestamp_ms': 1762663665453, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'K2EXPBLVMPOQTMDKWNVEAGBV', 'name': 'projects/ext-datasets/operations/K2EXPBLVMPOQTMDKWNVEAGBV'}\n","Grid 25\n","curr_year 2018\n","Saving data for Birbhum 2018\n","Task Started {'state': 'READY', 'description': 'Birbhum_25_2018', 'priority': 100, 'creation_timestamp_ms': 1762663673125, 'update_timestamp_ms': 1762663673125, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'NAAECTEI52Q7LNPEVTHHD7UJ', 'name': 'projects/ext-datasets/operations/NAAECTEI52Q7LNPEVTHHD7UJ'}\n","Grid 26\n","curr_year 2018\n","Saving data for Birbhum 2018\n","Task Started {'state': 'READY', 'description': 'Birbhum_26_2018', 'priority': 100, 'creation_timestamp_ms': 1762663679756, 'update_timestamp_ms': 1762663679756, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'MWN2KXYJJ3YY5VETVU5DAGFX', 'name': 'projects/ext-datasets/operations/MWN2KXYJJ3YY5VETVU5DAGFX'}\n","Grid 27\n","curr_year 2018\n","Saving data for Birbhum 2018\n","Task Started {'state': 'READY', 'description': 'Birbhum_27_2018', 'priority': 100, 'creation_timestamp_ms': 1762663686456, 'update_timestamp_ms': 1762663686456, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '3AFPA3QT2ZYJ7UQXU7WDTA5X', 'name': 'projects/ext-datasets/operations/3AFPA3QT2ZYJ7UQXU7WDTA5X'}\n","Grid 28\n","curr_year 2018\n","Saving data for Birbhum 2018\n","Task Started {'state': 'READY', 'description': 'Birbhum_28_2018', 'priority': 100, 'creation_timestamp_ms': 1762663692690, 'update_timestamp_ms': 1762663692690, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'DB7DD2TIVCR5ZDNZFPEIGEP5', 'name': 'projects/ext-datasets/operations/DB7DD2TIVCR5ZDNZFPEIGEP5'}\n","Grid 29\n","curr_year 2018\n","Saving data for Birbhum 2018\n","Task Started {'state': 'READY', 'description': 'Birbhum_29_2018', 'priority': 100, 'creation_timestamp_ms': 1762663698208, 'update_timestamp_ms': 1762663698208, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'QPE57Y37LNDBQHGESKKDRBVQ', 'name': 'projects/ext-datasets/operations/QPE57Y37LNDBQHGESKKDRBVQ'}\n","Grid 30\n","curr_year 2018\n","Saving data for Birbhum 2018\n","Task Started {'state': 'READY', 'description': 'Birbhum_30_2018', 'priority': 100, 'creation_timestamp_ms': 1762663702674, 'update_timestamp_ms': 1762663702674, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'Y5I4DFMH7NH3XBFOXEXQPXKL', 'name': 'projects/ext-datasets/operations/Y5I4DFMH7NH3XBFOXEXQPXKL'}\n","Grid 31\n","curr_year 2018\n","Saving data for Birbhum 2018\n","Task Started {'state': 'READY', 'description': 'Birbhum_31_2018', 'priority': 100, 'creation_timestamp_ms': 1762663711360, 'update_timestamp_ms': 1762663711360, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'PWM5YTRG3DPYRZGLX4QFGZT5', 'name': 'projects/ext-datasets/operations/PWM5YTRG3DPYRZGLX4QFGZT5'}\n","Grid 32\n","curr_year 2018\n","Saving data for Birbhum 2018\n","Task Started {'state': 'READY', 'description': 'Birbhum_32_2018', 'priority': 100, 'creation_timestamp_ms': 1762663720094, 'update_timestamp_ms': 1762663720094, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'FIES3SUJYX3NLYX64UETMUTG', 'name': 'projects/ext-datasets/operations/FIES3SUJYX3NLYX64UETMUTG'}\n","Grid 33\n","curr_year 2018\n","Saving data for Birbhum 2018\n","Task Started {'state': 'READY', 'description': 'Birbhum_33_2018', 'priority': 100, 'creation_timestamp_ms': 1762663728790, 'update_timestamp_ms': 1762663728790, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'SC6VMP7WP5RHNJFSQ747BCQ2', 'name': 'projects/ext-datasets/operations/SC6VMP7WP5RHNJFSQ747BCQ2'}\n","Grid 34\n","curr_year 2018\n","Saving data for Birbhum 2018\n","Task Started {'state': 'READY', 'description': 'Birbhum_34_2018', 'priority': 100, 'creation_timestamp_ms': 1762663735030, 'update_timestamp_ms': 1762663735030, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'G6O3OU37XUQOKE4R5B6FXCYP', 'name': 'projects/ext-datasets/operations/G6O3OU37XUQOKE4R5B6FXCYP'}\n","Grid 35\n","curr_year 2018\n","Saving data for Birbhum 2018\n","Task Started {'state': 'READY', 'description': 'Birbhum_35_2018', 'priority': 100, 'creation_timestamp_ms': 1762663744081, 'update_timestamp_ms': 1762663744081, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '7MP5LZ7IH4O5TWXMEBP7JVIS', 'name': 'projects/ext-datasets/operations/7MP5LZ7IH4O5TWXMEBP7JVIS'}\n","Grid 36\n","curr_year 2018\n","Saving data for Birbhum 2018\n","Task Started {'state': 'READY', 'description': 'Birbhum_36_2018', 'priority': 100, 'creation_timestamp_ms': 1762663751712, 'update_timestamp_ms': 1762663751712, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'USS54YJTU7N4VZ5VGUMYYT7P', 'name': 'projects/ext-datasets/operations/USS54YJTU7N4VZ5VGUMYYT7P'}\n","Grid 37\n","curr_year 2018\n","Saving data for Birbhum 2018\n","Task Started {'state': 'READY', 'description': 'Birbhum_37_2018', 'priority': 100, 'creation_timestamp_ms': 1762663761134, 'update_timestamp_ms': 1762663761134, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'ECL6WSNZMH5ICPE4VTV72YRL', 'name': 'projects/ext-datasets/operations/ECL6WSNZMH5ICPE4VTV72YRL'}\n","Grid 38\n","curr_year 2018\n","Saving data for Birbhum 2018\n","Task Started {'state': 'READY', 'description': 'Birbhum_38_2018', 'priority': 100, 'creation_timestamp_ms': 1762663767823, 'update_timestamp_ms': 1762663767823, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'EB2POV4JCEZ6QQ56VB7BCKJG', 'name': 'projects/ext-datasets/operations/EB2POV4JCEZ6QQ56VB7BCKJG'}\n","Grid 39\n","curr_year 2018\n","Saving data for Birbhum 2018\n","Task Started {'state': 'READY', 'description': 'Birbhum_39_2018', 'priority': 100, 'creation_timestamp_ms': 1762663776544, 'update_timestamp_ms': 1762663776544, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'NHY7CBINS7J2ZJTF4LFPZ4UV', 'name': 'projects/ext-datasets/operations/NHY7CBINS7J2ZJTF4LFPZ4UV'}\n","Grid 40\n","curr_year 2018\n","Saving data for Birbhum 2018\n","Task Started {'state': 'READY', 'description': 'Birbhum_40_2018', 'priority': 100, 'creation_timestamp_ms': 1762663781778, 'update_timestamp_ms': 1762663781778, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'TEDC6SJ6XJVK2XVAIW7XKFUD', 'name': 'projects/ext-datasets/operations/TEDC6SJ6XJVK2XVAIW7XKFUD'}\n","Grid 41\n","curr_year 2018\n","Saving data for Birbhum 2018\n","Task Started {'state': 'READY', 'description': 'Birbhum_41_2018', 'priority': 100, 'creation_timestamp_ms': 1762663789800, 'update_timestamp_ms': 1762663789800, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'KS5GS7FHXPGJONAIHTEVHYC7', 'name': 'projects/ext-datasets/operations/KS5GS7FHXPGJONAIHTEVHYC7'}\n","Grid 42\n","curr_year 2018\n","Saving data for Birbhum 2018\n","Task Started {'state': 'READY', 'description': 'Birbhum_42_2018', 'priority': 100, 'creation_timestamp_ms': 1762663796702, 'update_timestamp_ms': 1762663796702, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'PSF7PEPBP6GDPHXHGVAGWECG', 'name': 'projects/ext-datasets/operations/PSF7PEPBP6GDPHXHGVAGWECG'}\n","Grid 43\n","curr_year 2018\n","Saving data for Birbhum 2018\n","Task Started {'state': 'READY', 'description': 'Birbhum_43_2018', 'priority': 100, 'creation_timestamp_ms': 1762663805676, 'update_timestamp_ms': 1762663805676, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'WWNGLA4RHU5YPSV4HOQZBDUC', 'name': 'projects/ext-datasets/operations/WWNGLA4RHU5YPSV4HOQZBDUC'}\n","Grid 44\n","curr_year 2018\n","Saving data for Birbhum 2018\n","Task Started {'state': 'READY', 'description': 'Birbhum_44_2018', 'priority': 100, 'creation_timestamp_ms': 1762663810992, 'update_timestamp_ms': 1762663810992, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'A3MHZDDTEW4RGWEDX5UCQMZH', 'name': 'projects/ext-datasets/operations/A3MHZDDTEW4RGWEDX5UCQMZH'}\n","Grid 45\n","curr_year 2018\n","Saving data for Birbhum 2018\n","Task Started {'state': 'READY', 'description': 'Birbhum_45_2018', 'priority': 100, 'creation_timestamp_ms': 1762663819632, 'update_timestamp_ms': 1762663819632, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'WD6EHBLYUYLE5226KXJ45MUD', 'name': 'projects/ext-datasets/operations/WD6EHBLYUYLE5226KXJ45MUD'}\n","Grid 46\n","curr_year 2018\n","Saving data for Birbhum 2018\n","Task Started {'state': 'READY', 'description': 'Birbhum_46_2018', 'priority': 100, 'creation_timestamp_ms': 1762663826176, 'update_timestamp_ms': 1762663826176, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'VKXFLHYNJWRUNQFS4ZA3G6AZ', 'name': 'projects/ext-datasets/operations/VKXFLHYNJWRUNQFS4ZA3G6AZ'}\n","Grid 47\n","curr_year 2018\n","Saving data for Birbhum 2018\n","Task Started {'state': 'READY', 'description': 'Birbhum_47_2018', 'priority': 100, 'creation_timestamp_ms': 1762663833140, 'update_timestamp_ms': 1762663833140, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'PDHXEB24S7SAVVKYDYL2C4YE', 'name': 'projects/ext-datasets/operations/PDHXEB24S7SAVVKYDYL2C4YE'}\n","Grid 48\n","curr_year 2018\n","Saving data for Birbhum 2018\n","Task Started {'state': 'READY', 'description': 'Birbhum_48_2018', 'priority': 100, 'creation_timestamp_ms': 1762663841579, 'update_timestamp_ms': 1762663841579, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'SH5IKCYI6ORNBHBUQYG5CE5X', 'name': 'projects/ext-datasets/operations/SH5IKCYI6ORNBHBUQYG5CE5X'}\n","Grid 49\n","curr_year 2018\n","Saving data for Birbhum 2018\n","Task Started {'state': 'READY', 'description': 'Birbhum_49_2018', 'priority': 100, 'creation_timestamp_ms': 1762663849742, 'update_timestamp_ms': 1762663849742, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'WXGHZKERQ3WE3HTYGOVT6RVE', 'name': 'projects/ext-datasets/operations/WXGHZKERQ3WE3HTYGOVT6RVE'}\n","Grid 50\n","curr_year 2018\n","Saving data for Birbhum 2018\n","Task Started {'state': 'READY', 'description': 'Birbhum_50_2018', 'priority': 100, 'creation_timestamp_ms': 1762663857103, 'update_timestamp_ms': 1762663857103, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'B3JD2OLYY4LQTWG67I2ZUMNY', 'name': 'projects/ext-datasets/operations/B3JD2OLYY4LQTWG67I2ZUMNY'}\n","Grid 51\n","curr_year 2018\n","Saving data for Birbhum 2018\n","Task Started {'state': 'READY', 'description': 'Birbhum_51_2018', 'priority': 100, 'creation_timestamp_ms': 1762663863121, 'update_timestamp_ms': 1762663863121, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'ZMLF5A446ZHJUZCDKADL5QNA', 'name': 'projects/ext-datasets/operations/ZMLF5A446ZHJUZCDKADL5QNA'}\n","Grid 52\n","curr_year 2018\n","Saving data for Birbhum 2018\n","Task Started {'state': 'READY', 'description': 'Birbhum_52_2018', 'priority': 100, 'creation_timestamp_ms': 1762663870721, 'update_timestamp_ms': 1762663870721, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '56OHJBDKRWXYNPQHH6MFMBG3', 'name': 'projects/ext-datasets/operations/56OHJBDKRWXYNPQHH6MFMBG3'}\n","Grid 53\n","curr_year 2018\n","Saving data for Birbhum 2018\n","Task Started {'state': 'READY', 'description': 'Birbhum_53_2018', 'priority': 100, 'creation_timestamp_ms': 1762663874076, 'update_timestamp_ms': 1762663874076, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'RU46ZESGMS4Q3ATK26TVNIHS', 'name': 'projects/ext-datasets/operations/RU46ZESGMS4Q3ATK26TVNIHS'}\n","Grid 54\n","curr_year 2018\n","Saving data for Birbhum 2018\n","Task Started {'state': 'READY', 'description': 'Birbhum_54_2018', 'priority': 100, 'creation_timestamp_ms': 1762663883001, 'update_timestamp_ms': 1762663883001, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '65TAOCLGJN6DU7XGB4GZKRKK', 'name': 'projects/ext-datasets/operations/65TAOCLGJN6DU7XGB4GZKRKK'}\n","Grid 55\n","curr_year 2018\n","Saving data for Birbhum 2018\n","Task Started {'state': 'READY', 'description': 'Birbhum_55_2018', 'priority': 100, 'creation_timestamp_ms': 1762663893940, 'update_timestamp_ms': 1762663893940, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'T5A7UTYK2VM3UFMM3MJ6BXRW', 'name': 'projects/ext-datasets/operations/T5A7UTYK2VM3UFMM3MJ6BXRW'}\n","Grid 56\n","curr_year 2018\n","Saving data for Birbhum 2018\n","Task Started {'state': 'READY', 'description': 'Birbhum_56_2018', 'priority': 100, 'creation_timestamp_ms': 1762663899357, 'update_timestamp_ms': 1762663899357, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'P6K3Q6R7ZC3MERGKZ6QXYM2N', 'name': 'projects/ext-datasets/operations/P6K3Q6R7ZC3MERGKZ6QXYM2N'}\n","Grid 57\n","curr_year 2018\n","Saving data for Birbhum 2018\n","Task Started {'state': 'READY', 'description': 'Birbhum_57_2018', 'priority': 100, 'creation_timestamp_ms': 1762663907279, 'update_timestamp_ms': 1762663907279, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'RJH7DNMXHIH6PDL66LZFJCKS', 'name': 'projects/ext-datasets/operations/RJH7DNMXHIH6PDL66LZFJCKS'}\n","Grid 58\n","curr_year 2018\n","Saving data for Birbhum 2018\n","Task Started {'state': 'READY', 'description': 'Birbhum_58_2018', 'priority': 100, 'creation_timestamp_ms': 1762663912561, 'update_timestamp_ms': 1762663912561, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '4MXJ5EPJBGOMAWEAK6BDHVGE', 'name': 'projects/ext-datasets/operations/4MXJ5EPJBGOMAWEAK6BDHVGE'}\n","Grid 59\n","curr_year 2018\n","Saving data for Birbhum 2018\n","Task Started {'state': 'READY', 'description': 'Birbhum_59_2018', 'priority': 100, 'creation_timestamp_ms': 1762663920998, 'update_timestamp_ms': 1762663920998, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '57HVEPIIR4JEKBG7J4T2FRLD', 'name': 'projects/ext-datasets/operations/57HVEPIIR4JEKBG7J4T2FRLD'}\n","Grid 60\n","curr_year 2018\n","Saving data for Birbhum 2018\n","Task Started {'state': 'READY', 'description': 'Birbhum_60_2018', 'priority': 100, 'creation_timestamp_ms': 1762663925300, 'update_timestamp_ms': 1762663925300, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'ZB6N4YQM353T4CZBYLVUAH3D', 'name': 'projects/ext-datasets/operations/ZB6N4YQM353T4CZBYLVUAH3D'}\n","Grid 61\n","curr_year 2018\n","Saving data for Birbhum 2018\n","Task Started {'state': 'READY', 'description': 'Birbhum_61_2018', 'priority': 100, 'creation_timestamp_ms': 1762663933407, 'update_timestamp_ms': 1762663933407, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'VHTIGKFDTV7OMYGIBLDCAV22', 'name': 'projects/ext-datasets/operations/VHTIGKFDTV7OMYGIBLDCAV22'}\n","Grid 62\n","curr_year 2018\n","Saving data for Birbhum 2018\n","Task Started {'state': 'READY', 'description': 'Birbhum_62_2018', 'priority': 100, 'creation_timestamp_ms': 1762663942021, 'update_timestamp_ms': 1762663942021, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'NH2SNA63OXC76IN3WHWLEZ5T', 'name': 'projects/ext-datasets/operations/NH2SNA63OXC76IN3WHWLEZ5T'}\n","Grid 63\n","curr_year 2018\n","Saving data for Birbhum 2018\n","Task Started {'state': 'READY', 'description': 'Birbhum_63_2018', 'priority': 100, 'creation_timestamp_ms': 1762663949713, 'update_timestamp_ms': 1762663949713, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'NFW6EP2F7TWXCGZTGFKSNJJA', 'name': 'projects/ext-datasets/operations/NFW6EP2F7TWXCGZTGFKSNJJA'}\n","Grid 64\n","curr_year 2018\n","Saving data for Birbhum 2018\n","Task Started {'state': 'READY', 'description': 'Birbhum_64_2018', 'priority': 100, 'creation_timestamp_ms': 1762663972275, 'update_timestamp_ms': 1762663972275, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'IH6EHYSF6RICKGLTJXLEJTQT', 'name': 'projects/ext-datasets/operations/IH6EHYSF6RICKGLTJXLEJTQT'}\n","Grid 65\n","curr_year 2018\n","Saving data for Birbhum 2018\n","Task Started {'state': 'READY', 'description': 'Birbhum_65_2018', 'priority': 100, 'creation_timestamp_ms': 1762663978337, 'update_timestamp_ms': 1762663978337, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'IMD2EJILY3GBGP4ODN56RI3B', 'name': 'projects/ext-datasets/operations/IMD2EJILY3GBGP4ODN56RI3B'}\n","Grid 66\n","curr_year 2018\n","Saving data for Birbhum 2018\n","Task Started {'state': 'READY', 'description': 'Birbhum_66_2018', 'priority': 100, 'creation_timestamp_ms': 1762663988697, 'update_timestamp_ms': 1762663988697, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'YWQTVH5IYEM4S2OKPSPWIAPH', 'name': 'projects/ext-datasets/operations/YWQTVH5IYEM4S2OKPSPWIAPH'}\n","Grid 67\n","curr_year 2018\n","Saving data for Birbhum 2018\n","Task Started {'state': 'READY', 'description': 'Birbhum_67_2018', 'priority': 100, 'creation_timestamp_ms': 1762663996373, 'update_timestamp_ms': 1762663996373, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '27N3EXCI3VARLUI2SDOTE5BR', 'name': 'projects/ext-datasets/operations/27N3EXCI3VARLUI2SDOTE5BR'}\n","Grid 68\n","curr_year 2018\n","Saving data for Birbhum 2018\n","Task Started {'state': 'READY', 'description': 'Birbhum_68_2018', 'priority': 100, 'creation_timestamp_ms': 1762664004815, 'update_timestamp_ms': 1762664004815, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'VL5AF6S4AOX7G7VGACADXMVJ', 'name': 'projects/ext-datasets/operations/VL5AF6S4AOX7G7VGACADXMVJ'}\n","Grid 69\n","curr_year 2018\n","Saving data for Birbhum 2018\n","Task Started {'state': 'READY', 'description': 'Birbhum_69_2018', 'priority': 100, 'creation_timestamp_ms': 1762664014708, 'update_timestamp_ms': 1762664014708, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'VLXNSDOSANSXYUJEFDHAPQ3B', 'name': 'projects/ext-datasets/operations/VLXNSDOSANSXYUJEFDHAPQ3B'}\n","Grid 70\n","curr_year 2018\n","Saving data for Birbhum 2018\n","Task Started {'state': 'READY', 'description': 'Birbhum_70_2018', 'priority': 100, 'creation_timestamp_ms': 1762664024423, 'update_timestamp_ms': 1762664024423, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'T35LURRRU3UHVLSHWZ66PHIU', 'name': 'projects/ext-datasets/operations/T35LURRRU3UHVLSHWZ66PHIU'}\n","Grid 71\n","curr_year 2018\n","Saving data for Birbhum 2018\n","Task Started {'state': 'READY', 'description': 'Birbhum_71_2018', 'priority': 100, 'creation_timestamp_ms': 1762664032431, 'update_timestamp_ms': 1762664032431, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'FYRKQWWRXKIXHX6X3DMGVIGE', 'name': 'projects/ext-datasets/operations/FYRKQWWRXKIXHX6X3DMGVIGE'}\n","Grid 72\n","curr_year 2018\n","Saving data for Birbhum 2018\n","Task Started {'state': 'READY', 'description': 'Birbhum_72_2018', 'priority': 100, 'creation_timestamp_ms': 1762664036998, 'update_timestamp_ms': 1762664036998, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'HILZGFL3KWGDCJA4OPKU2FHR', 'name': 'projects/ext-datasets/operations/HILZGFL3KWGDCJA4OPKU2FHR'}\n","Grid 73\n","curr_year 2018\n","Saving data for Birbhum 2018\n","Task Started {'state': 'READY', 'description': 'Birbhum_73_2018', 'priority': 100, 'creation_timestamp_ms': 1762664045448, 'update_timestamp_ms': 1762664045448, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'JAOF2L5EJ7VBNBN5GYFKVKBQ', 'name': 'projects/ext-datasets/operations/JAOF2L5EJ7VBNBN5GYFKVKBQ'}\n","Grid 74\n","curr_year 2018\n","Saving data for Birbhum 2018\n","Task Started {'state': 'READY', 'description': 'Birbhum_74_2018', 'priority': 100, 'creation_timestamp_ms': 1762664051637, 'update_timestamp_ms': 1762664051637, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'AGA2LYPNRCWEDRBYJWRAPOLH', 'name': 'projects/ext-datasets/operations/AGA2LYPNRCWEDRBYJWRAPOLH'}\n","Grid 75\n","curr_year 2018\n","Saving data for Birbhum 2018\n","Task Started {'state': 'READY', 'description': 'Birbhum_75_2018', 'priority': 100, 'creation_timestamp_ms': 1762664059346, 'update_timestamp_ms': 1762664059346, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'YO4SQXJZQKL4ZMHHZXF3BFNU', 'name': 'projects/ext-datasets/operations/YO4SQXJZQKL4ZMHHZXF3BFNU'}\n","2821.194188594818\n","Year 2018, District 14: Dakshin Dinajpur, grids: 37\n","Grid 0\n","curr_year 2018\n","Saving data for Dakshin Dinajpur 2018\n","Task Started {'state': 'READY', 'description': 'Dakshin Dinajpur_0_2018', 'priority': 100, 'creation_timestamp_ms': 1762664073044, 'update_timestamp_ms': 1762664073044, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'WRSC7TBNVGKGUOVTS2YX5GSS', 'name': 'projects/ext-datasets/operations/WRSC7TBNVGKGUOVTS2YX5GSS'}\n","Grid 1\n","curr_year 2018\n","Saving data for Dakshin Dinajpur 2018\n","Task Started {'state': 'READY', 'description': 'Dakshin Dinajpur_1_2018', 'priority': 100, 'creation_timestamp_ms': 1762664080530, 'update_timestamp_ms': 1762664080530, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'ZR4WASTTV4KDNTKVN7M4YDLR', 'name': 'projects/ext-datasets/operations/ZR4WASTTV4KDNTKVN7M4YDLR'}\n","Grid 2\n","curr_year 2018\n","Saving data for Dakshin Dinajpur 2018\n","Task Started {'state': 'READY', 'description': 'Dakshin Dinajpur_2_2018', 'priority': 100, 'creation_timestamp_ms': 1762664088096, 'update_timestamp_ms': 1762664088096, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'A2R342BUIFTPK3BF4B37PWVH', 'name': 'projects/ext-datasets/operations/A2R342BUIFTPK3BF4B37PWVH'}\n","Grid 3\n","curr_year 2018\n","Saving data for Dakshin Dinajpur 2018\n","Task Started {'state': 'READY', 'description': 'Dakshin Dinajpur_3_2018', 'priority': 100, 'creation_timestamp_ms': 1762664096150, 'update_timestamp_ms': 1762664096150, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'FCNGYVV6INADTDCGBVXAZZY5', 'name': 'projects/ext-datasets/operations/FCNGYVV6INADTDCGBVXAZZY5'}\n","Grid 4\n","curr_year 2018\n","Saving data for Dakshin Dinajpur 2018\n","Task Started {'state': 'READY', 'description': 'Dakshin Dinajpur_4_2018', 'priority': 100, 'creation_timestamp_ms': 1762664103831, 'update_timestamp_ms': 1762664103831, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '62464TYAT2PKXK5YCIFPFIXQ', 'name': 'projects/ext-datasets/operations/62464TYAT2PKXK5YCIFPFIXQ'}\n","Grid 5\n","curr_year 2018\n","Saving data for Dakshin Dinajpur 2018\n","Task Started {'state': 'READY', 'description': 'Dakshin Dinajpur_5_2018', 'priority': 100, 'creation_timestamp_ms': 1762664112856, 'update_timestamp_ms': 1762664112856, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'EOTDLUXVVPCIHRDRPOCJ7AKP', 'name': 'projects/ext-datasets/operations/EOTDLUXVVPCIHRDRPOCJ7AKP'}\n","Grid 6\n","curr_year 2018\n","Saving data for Dakshin Dinajpur 2018\n","Task Started {'state': 'READY', 'description': 'Dakshin Dinajpur_6_2018', 'priority': 100, 'creation_timestamp_ms': 1762664118061, 'update_timestamp_ms': 1762664118061, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'WYKD5KNHUOYOZFWMDXNBSXCA', 'name': 'projects/ext-datasets/operations/WYKD5KNHUOYOZFWMDXNBSXCA'}\n","Grid 7\n","curr_year 2018\n","Saving data for Dakshin Dinajpur 2018\n","Task Started {'state': 'READY', 'description': 'Dakshin Dinajpur_7_2018', 'priority': 100, 'creation_timestamp_ms': 1762664122228, 'update_timestamp_ms': 1762664122228, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '53UAS25DUR45DOW7S2ZIHAZE', 'name': 'projects/ext-datasets/operations/53UAS25DUR45DOW7S2ZIHAZE'}\n","Grid 8\n","curr_year 2018\n","Saving data for Dakshin Dinajpur 2018\n","Task Started {'state': 'READY', 'description': 'Dakshin Dinajpur_8_2018', 'priority': 100, 'creation_timestamp_ms': 1762664129592, 'update_timestamp_ms': 1762664129592, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'LKLX7A5H2IISEGQOMIUDJIZM', 'name': 'projects/ext-datasets/operations/LKLX7A5H2IISEGQOMIUDJIZM'}\n","Grid 9\n","curr_year 2018\n","Saving data for Dakshin Dinajpur 2018\n","Task Started {'state': 'READY', 'description': 'Dakshin Dinajpur_9_2018', 'priority': 100, 'creation_timestamp_ms': 1762664138306, 'update_timestamp_ms': 1762664138306, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'ORDPCTPYHGFF2733LNV3ZPJG', 'name': 'projects/ext-datasets/operations/ORDPCTPYHGFF2733LNV3ZPJG'}\n","Grid 10\n","curr_year 2018\n","Saving data for Dakshin Dinajpur 2018\n","Task Started {'state': 'READY', 'description': 'Dakshin Dinajpur_10_2018', 'priority': 100, 'creation_timestamp_ms': 1762664148417, 'update_timestamp_ms': 1762664148417, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'KZ3ABRVLLCDG2T72JU7OIEDH', 'name': 'projects/ext-datasets/operations/KZ3ABRVLLCDG2T72JU7OIEDH'}\n","Grid 11\n","curr_year 2018\n","Saving data for Dakshin Dinajpur 2018\n","Task Started {'state': 'READY', 'description': 'Dakshin Dinajpur_11_2018', 'priority': 100, 'creation_timestamp_ms': 1762664155360, 'update_timestamp_ms': 1762664155360, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'ATP2S4CRFW667RUCR4KU522Z', 'name': 'projects/ext-datasets/operations/ATP2S4CRFW667RUCR4KU522Z'}\n","Grid 12\n","curr_year 2018\n","Saving data for Dakshin Dinajpur 2018\n","Task Started {'state': 'READY', 'description': 'Dakshin Dinajpur_12_2018', 'priority': 100, 'creation_timestamp_ms': 1762664162579, 'update_timestamp_ms': 1762664162579, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'IWAHODRCOHSRS2ICK5ES4PLW', 'name': 'projects/ext-datasets/operations/IWAHODRCOHSRS2ICK5ES4PLW'}\n","Grid 13\n","curr_year 2018\n","Saving data for Dakshin Dinajpur 2018\n","Task Started {'state': 'READY', 'description': 'Dakshin Dinajpur_13_2018', 'priority': 100, 'creation_timestamp_ms': 1762664170862, 'update_timestamp_ms': 1762664170862, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'A3SMQDKTQZFSFDD4YFSNOC5U', 'name': 'projects/ext-datasets/operations/A3SMQDKTQZFSFDD4YFSNOC5U'}\n","Grid 14\n","curr_year 2018\n","Saving data for Dakshin Dinajpur 2018\n","Task Started {'state': 'READY', 'description': 'Dakshin Dinajpur_14_2018', 'priority': 100, 'creation_timestamp_ms': 1762664183395, 'update_timestamp_ms': 1762664183395, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'I4SEFYNGSRJX2CA3MXLUSE7L', 'name': 'projects/ext-datasets/operations/I4SEFYNGSRJX2CA3MXLUSE7L'}\n","Grid 15\n","curr_year 2018\n","Saving data for Dakshin Dinajpur 2018\n","Task Started {'state': 'READY', 'description': 'Dakshin Dinajpur_15_2018', 'priority': 100, 'creation_timestamp_ms': 1762664191470, 'update_timestamp_ms': 1762664191470, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'ZNXGGZKBUGUTRCAK4O7K2ORW', 'name': 'projects/ext-datasets/operations/ZNXGGZKBUGUTRCAK4O7K2ORW'}\n","Grid 16\n","curr_year 2018\n","Saving data for Dakshin Dinajpur 2018\n","Task Started {'state': 'READY', 'description': 'Dakshin Dinajpur_16_2018', 'priority': 100, 'creation_timestamp_ms': 1762664196031, 'update_timestamp_ms': 1762664196031, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '2TJN6PNP3HR77NNJ5R2PVVW5', 'name': 'projects/ext-datasets/operations/2TJN6PNP3HR77NNJ5R2PVVW5'}\n","Grid 17\n","curr_year 2018\n","Saving data for Dakshin Dinajpur 2018\n","Task Started {'state': 'READY', 'description': 'Dakshin Dinajpur_17_2018', 'priority': 100, 'creation_timestamp_ms': 1762664202924, 'update_timestamp_ms': 1762664202924, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'L7AM2H3WPXAYWGDJTRM3HYFO', 'name': 'projects/ext-datasets/operations/L7AM2H3WPXAYWGDJTRM3HYFO'}\n","Grid 18\n","curr_year 2018\n","Saving data for Dakshin Dinajpur 2018\n","Task Started {'state': 'READY', 'description': 'Dakshin Dinajpur_18_2018', 'priority': 100, 'creation_timestamp_ms': 1762664217745, 'update_timestamp_ms': 1762664217745, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'UXFAB6GZRTW2HJIHSQO72GOT', 'name': 'projects/ext-datasets/operations/UXFAB6GZRTW2HJIHSQO72GOT'}\n","Grid 19\n","curr_year 2018\n","Saving data for Dakshin Dinajpur 2018\n","Task Started {'state': 'READY', 'description': 'Dakshin Dinajpur_19_2018', 'priority': 100, 'creation_timestamp_ms': 1762664226176, 'update_timestamp_ms': 1762664226176, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'XDKZM4CYU7JXGIMQF3YY4PWD', 'name': 'projects/ext-datasets/operations/XDKZM4CYU7JXGIMQF3YY4PWD'}\n","Grid 20\n","curr_year 2018\n","Saving data for Dakshin Dinajpur 2018\n","Task Started {'state': 'READY', 'description': 'Dakshin Dinajpur_20_2018', 'priority': 100, 'creation_timestamp_ms': 1762664233704, 'update_timestamp_ms': 1762664233704, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '2SZDRF7WC5X7656JH7KDZ3WF', 'name': 'projects/ext-datasets/operations/2SZDRF7WC5X7656JH7KDZ3WF'}\n","Grid 21\n","curr_year 2018\n","Saving data for Dakshin Dinajpur 2018\n","Task Started {'state': 'READY', 'description': 'Dakshin Dinajpur_21_2018', 'priority': 100, 'creation_timestamp_ms': 1762664248279, 'update_timestamp_ms': 1762664248279, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '3KD3BG3HIHJUROC6RCMQP4S4', 'name': 'projects/ext-datasets/operations/3KD3BG3HIHJUROC6RCMQP4S4'}\n","Grid 22\n","curr_year 2018\n","Saving data for Dakshin Dinajpur 2018\n","Task Started {'state': 'READY', 'description': 'Dakshin Dinajpur_22_2018', 'priority': 100, 'creation_timestamp_ms': 1762664255705, 'update_timestamp_ms': 1762664255705, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'XVSNVNMLA4BPOVAVCGDCBSUP', 'name': 'projects/ext-datasets/operations/XVSNVNMLA4BPOVAVCGDCBSUP'}\n","Grid 23\n","curr_year 2018\n","Saving data for Dakshin Dinajpur 2018\n","Task Started {'state': 'READY', 'description': 'Dakshin Dinajpur_23_2018', 'priority': 100, 'creation_timestamp_ms': 1762664271104, 'update_timestamp_ms': 1762664271104, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'KK2XZ6YFYF7H6IQ4Z3V2K6FR', 'name': 'projects/ext-datasets/operations/KK2XZ6YFYF7H6IQ4Z3V2K6FR'}\n","Grid 24\n","curr_year 2018\n","Saving data for Dakshin Dinajpur 2018\n","Task Started {'state': 'READY', 'description': 'Dakshin Dinajpur_24_2018', 'priority': 100, 'creation_timestamp_ms': 1762664279095, 'update_timestamp_ms': 1762664279095, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'PL52ONVJZD7PKQNS2TBFND6J', 'name': 'projects/ext-datasets/operations/PL52ONVJZD7PKQNS2TBFND6J'}\n","Grid 25\n","curr_year 2018\n","Saving data for Dakshin Dinajpur 2018\n","Task Started {'state': 'READY', 'description': 'Dakshin Dinajpur_25_2018', 'priority': 100, 'creation_timestamp_ms': 1762664284347, 'update_timestamp_ms': 1762664284347, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'UVMEMENXU3HOR43V6NHO6OM6', 'name': 'projects/ext-datasets/operations/UVMEMENXU3HOR43V6NHO6OM6'}\n","Grid 26\n","curr_year 2018\n","Saving data for Dakshin Dinajpur 2018\n","Task Started {'state': 'READY', 'description': 'Dakshin Dinajpur_26_2018', 'priority': 100, 'creation_timestamp_ms': 1762664292053, 'update_timestamp_ms': 1762664292053, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '5VGYNHRU5MM77ZVACEZAI6SY', 'name': 'projects/ext-datasets/operations/5VGYNHRU5MM77ZVACEZAI6SY'}\n","Grid 27\n","curr_year 2018\n","Saving data for Dakshin Dinajpur 2018\n","Task Started {'state': 'READY', 'description': 'Dakshin Dinajpur_27_2018', 'priority': 100, 'creation_timestamp_ms': 1762664298908, 'update_timestamp_ms': 1762664298908, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '6TV5UZ564K3RZ6RULWKZZSFN', 'name': 'projects/ext-datasets/operations/6TV5UZ564K3RZ6RULWKZZSFN'}\n","Grid 28\n","curr_year 2018\n","Saving data for Dakshin Dinajpur 2018\n","Task Started {'state': 'READY', 'description': 'Dakshin Dinajpur_28_2018', 'priority': 100, 'creation_timestamp_ms': 1762664303868, 'update_timestamp_ms': 1762664303868, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'PPRLKYSXBFI6F3XSSB2CALXJ', 'name': 'projects/ext-datasets/operations/PPRLKYSXBFI6F3XSSB2CALXJ'}\n","Grid 29\n","curr_year 2018\n","Saving data for Dakshin Dinajpur 2018\n","Task Started {'state': 'READY', 'description': 'Dakshin Dinajpur_29_2018', 'priority': 100, 'creation_timestamp_ms': 1762664311473, 'update_timestamp_ms': 1762664311473, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '4QKGB674VY46L6HHLFTAUDHK', 'name': 'projects/ext-datasets/operations/4QKGB674VY46L6HHLFTAUDHK'}\n","Grid 30\n","curr_year 2018\n","Saving data for Dakshin Dinajpur 2018\n","Task Started {'state': 'READY', 'description': 'Dakshin Dinajpur_30_2018', 'priority': 100, 'creation_timestamp_ms': 1762664318310, 'update_timestamp_ms': 1762664318310, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '4F7BB7DJRPVBHCEVQ6ZV6OSN', 'name': 'projects/ext-datasets/operations/4F7BB7DJRPVBHCEVQ6ZV6OSN'}\n","Grid 31\n","curr_year 2018\n","Saving data for Dakshin Dinajpur 2018\n","Task Started {'state': 'READY', 'description': 'Dakshin Dinajpur_31_2018', 'priority': 100, 'creation_timestamp_ms': 1762664325371, 'update_timestamp_ms': 1762664325371, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'LFXTJ4MDTBICB2J54A6NII7A', 'name': 'projects/ext-datasets/operations/LFXTJ4MDTBICB2J54A6NII7A'}\n","Grid 32\n","curr_year 2018\n","Saving data for Dakshin Dinajpur 2018\n","Task Started {'state': 'READY', 'description': 'Dakshin Dinajpur_32_2018', 'priority': 100, 'creation_timestamp_ms': 1762664331713, 'update_timestamp_ms': 1762664331713, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'UGEKFPPPWSI6S2QECYLXUAIS', 'name': 'projects/ext-datasets/operations/UGEKFPPPWSI6S2QECYLXUAIS'}\n","Grid 33\n","curr_year 2018\n","Saving data for Dakshin Dinajpur 2018\n","Task Started {'state': 'READY', 'description': 'Dakshin Dinajpur_33_2018', 'priority': 100, 'creation_timestamp_ms': 1762664340044, 'update_timestamp_ms': 1762664340044, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'OKWS5AKSZBNIJMDQZPX3DO5I', 'name': 'projects/ext-datasets/operations/OKWS5AKSZBNIJMDQZPX3DO5I'}\n","Grid 34\n","curr_year 2018\n","Saving data for Dakshin Dinajpur 2018\n","Task Started {'state': 'READY', 'description': 'Dakshin Dinajpur_34_2018', 'priority': 100, 'creation_timestamp_ms': 1762664348038, 'update_timestamp_ms': 1762664348038, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '7B6CUKVIVPZLZH36SJJDIZC4', 'name': 'projects/ext-datasets/operations/7B6CUKVIVPZLZH36SJJDIZC4'}\n","Grid 35\n","curr_year 2018\n","Saving data for Dakshin Dinajpur 2018\n","Task Started {'state': 'READY', 'description': 'Dakshin Dinajpur_35_2018', 'priority': 100, 'creation_timestamp_ms': 1762664358407, 'update_timestamp_ms': 1762664358407, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'GGKEM47OSNSJ4M2QVJL3CLD4', 'name': 'projects/ext-datasets/operations/GGKEM47OSNSJ4M2QVJL3CLD4'}\n","Grid 36\n","curr_year 2018\n","Saving data for Dakshin Dinajpur 2018\n","Task Started {'state': 'READY', 'description': 'Dakshin Dinajpur_36_2018', 'priority': 100, 'creation_timestamp_ms': 1762664364512, 'update_timestamp_ms': 1762664364512, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'ZI7LHFRUCDLSECQFOS6JAI2Q', 'name': 'projects/ext-datasets/operations/ZI7LHFRUCDLSECQFOS6JAI2Q'}\n","3126.3451647758484\n","Year 2018, District 15: Darjiling, grids: 2\n","Grid 0\n","curr_year 2018\n","Saving data for Darjiling 2018\n","Task Started {'state': 'READY', 'description': 'Darjiling_0_2018', 'priority': 100, 'creation_timestamp_ms': 1762664376817, 'update_timestamp_ms': 1762664376817, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'IFZCUUXASEZZKFGGUCP2P5GU', 'name': 'projects/ext-datasets/operations/IFZCUUXASEZZKFGGUCP2P5GU'}\n","Grid 1\n","curr_year 2018\n","Saving data for Darjiling 2018\n","Task Started {'state': 'READY', 'description': 'Darjiling_1_2018', 'priority': 100, 'creation_timestamp_ms': 1762664383693, 'update_timestamp_ms': 1762664383693, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '44DTTT2UGSGPC5PJYRDSK4EV', 'name': 'projects/ext-datasets/operations/44DTTT2UGSGPC5PJYRDSK4EV'}\n","3145.506931781769\n","Year 2018, District 16: Haora, grids: 30\n","Grid 0\n","curr_year 2018\n","Saving data for Haora 2018\n","Task Started {'state': 'READY', 'description': 'Haora_0_2018', 'priority': 100, 'creation_timestamp_ms': 1762664398092, 'update_timestamp_ms': 1762664398092, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '4AJORJDRNG6LP53KMGIYCXMH', 'name': 'projects/ext-datasets/operations/4AJORJDRNG6LP53KMGIYCXMH'}\n","Grid 1\n","curr_year 2018\n","Saving data for Haora 2018\n","Task Started {'state': 'READY', 'description': 'Haora_1_2018', 'priority': 100, 'creation_timestamp_ms': 1762664405353, 'update_timestamp_ms': 1762664405353, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'DMGRBT4HG5WKSSM2CQDCFQGW', 'name': 'projects/ext-datasets/operations/DMGRBT4HG5WKSSM2CQDCFQGW'}\n","Grid 2\n","curr_year 2018\n","Saving data for Haora 2018\n","Task Started {'state': 'READY', 'description': 'Haora_2_2018', 'priority': 100, 'creation_timestamp_ms': 1762664412695, 'update_timestamp_ms': 1762664412695, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'XU43F4PQAWPNJRLWOC3PZTTZ', 'name': 'projects/ext-datasets/operations/XU43F4PQAWPNJRLWOC3PZTTZ'}\n","Grid 3\n","curr_year 2018\n","Saving data for Haora 2018\n","Task Started {'state': 'READY', 'description': 'Haora_3_2018', 'priority': 100, 'creation_timestamp_ms': 1762664419564, 'update_timestamp_ms': 1762664419564, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'MM4MFBBIP46U75JCTAWEF325', 'name': 'projects/ext-datasets/operations/MM4MFBBIP46U75JCTAWEF325'}\n","Grid 4\n","curr_year 2018\n","Saving data for Haora 2018\n","Task Started {'state': 'READY', 'description': 'Haora_4_2018', 'priority': 100, 'creation_timestamp_ms': 1762664427124, 'update_timestamp_ms': 1762664427124, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'IFIN6KZ4KF4JYO3WIOIZQEQK', 'name': 'projects/ext-datasets/operations/IFIN6KZ4KF4JYO3WIOIZQEQK'}\n","Grid 5\n","curr_year 2018\n","Saving data for Haora 2018\n","Task Started {'state': 'READY', 'description': 'Haora_5_2018', 'priority': 100, 'creation_timestamp_ms': 1762664434906, 'update_timestamp_ms': 1762664434906, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'PEZIF46GZ753LJRMW5V7EGWX', 'name': 'projects/ext-datasets/operations/PEZIF46GZ753LJRMW5V7EGWX'}\n","Grid 6\n","curr_year 2018\n","Saving data for Haora 2018\n","Task Started {'state': 'READY', 'description': 'Haora_6_2018', 'priority': 100, 'creation_timestamp_ms': 1762664443151, 'update_timestamp_ms': 1762664443151, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'DLVEWDCZKMIJQPVDADOVYUBT', 'name': 'projects/ext-datasets/operations/DLVEWDCZKMIJQPVDADOVYUBT'}\n","Grid 7\n","curr_year 2018\n","Saving data for Haora 2018\n","Task Started {'state': 'READY', 'description': 'Haora_7_2018', 'priority': 100, 'creation_timestamp_ms': 1762664448495, 'update_timestamp_ms': 1762664448495, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'K4QSIV3GJKSAUILFTVMPTPW3', 'name': 'projects/ext-datasets/operations/K4QSIV3GJKSAUILFTVMPTPW3'}\n","Grid 8\n","curr_year 2018\n","Saving data for Haora 2018\n","Task Started {'state': 'READY', 'description': 'Haora_8_2018', 'priority': 100, 'creation_timestamp_ms': 1762664453529, 'update_timestamp_ms': 1762664453529, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'SKMRID4ZKASKWRSPD5KOPS55', 'name': 'projects/ext-datasets/operations/SKMRID4ZKASKWRSPD5KOPS55'}\n","Grid 9\n","curr_year 2018\n","Saving data for Haora 2018\n","Task Started {'state': 'READY', 'description': 'Haora_9_2018', 'priority': 100, 'creation_timestamp_ms': 1762664463309, 'update_timestamp_ms': 1762664463309, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'YJTSZOTEFBWLQ2VR36PMUHCV', 'name': 'projects/ext-datasets/operations/YJTSZOTEFBWLQ2VR36PMUHCV'}\n","Grid 10\n","curr_year 2018\n","Saving data for Haora 2018\n","Task Started {'state': 'READY', 'description': 'Haora_10_2018', 'priority': 100, 'creation_timestamp_ms': 1762664469940, 'update_timestamp_ms': 1762664469940, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'LR5DDCWXWBLZWTTPTR6AB3HM', 'name': 'projects/ext-datasets/operations/LR5DDCWXWBLZWTTPTR6AB3HM'}\n","Grid 11\n","curr_year 2018\n","Saving data for Haora 2018\n","Task Started {'state': 'READY', 'description': 'Haora_11_2018', 'priority': 100, 'creation_timestamp_ms': 1762664478105, 'update_timestamp_ms': 1762664478105, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'QWBZ24PODX3TCUEW7SK56EKU', 'name': 'projects/ext-datasets/operations/QWBZ24PODX3TCUEW7SK56EKU'}\n","Grid 12\n","curr_year 2018\n","Saving data for Haora 2018\n","Task Started {'state': 'READY', 'description': 'Haora_12_2018', 'priority': 100, 'creation_timestamp_ms': 1762664485766, 'update_timestamp_ms': 1762664485766, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '6HQP2DXYEJ3RCR3PPY5SOHPZ', 'name': 'projects/ext-datasets/operations/6HQP2DXYEJ3RCR3PPY5SOHPZ'}\n","Grid 13\n","curr_year 2018\n","Saving data for Haora 2018\n","Task Started {'state': 'READY', 'description': 'Haora_13_2018', 'priority': 100, 'creation_timestamp_ms': 1762664493474, 'update_timestamp_ms': 1762664493474, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'V4CGS5URTKASIAASJOEYUTP7', 'name': 'projects/ext-datasets/operations/V4CGS5URTKASIAASJOEYUTP7'}\n","Grid 14\n","curr_year 2018\n","Saving data for Haora 2018\n","Task Started {'state': 'READY', 'description': 'Haora_14_2018', 'priority': 100, 'creation_timestamp_ms': 1762664500714, 'update_timestamp_ms': 1762664500714, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'JWHRKPTAWQLVGGKDBABX36A3', 'name': 'projects/ext-datasets/operations/JWHRKPTAWQLVGGKDBABX36A3'}\n","Grid 15\n","curr_year 2018\n","Saving data for Haora 2018\n","Task Started {'state': 'READY', 'description': 'Haora_15_2018', 'priority': 100, 'creation_timestamp_ms': 1762664504652, 'update_timestamp_ms': 1762664504652, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'IK765SYM32CH3J4YCKTH5YMT', 'name': 'projects/ext-datasets/operations/IK765SYM32CH3J4YCKTH5YMT'}\n","Grid 16\n","curr_year 2018\n","Saving data for Haora 2018\n","Task Started {'state': 'READY', 'description': 'Haora_16_2018', 'priority': 100, 'creation_timestamp_ms': 1762664512845, 'update_timestamp_ms': 1762664512845, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '66G25QAKNUVJJZU6K4GIAZLI', 'name': 'projects/ext-datasets/operations/66G25QAKNUVJJZU6K4GIAZLI'}\n","Grid 17\n","curr_year 2018\n","Saving data for Haora 2018\n","Task Started {'state': 'READY', 'description': 'Haora_17_2018', 'priority': 100, 'creation_timestamp_ms': 1762664519636, 'update_timestamp_ms': 1762664519636, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '4Q4APFMPM37CWZNDNBQIIVL6', 'name': 'projects/ext-datasets/operations/4Q4APFMPM37CWZNDNBQIIVL6'}\n","Grid 18\n","curr_year 2018\n","Saving data for Haora 2018\n","Task Started {'state': 'READY', 'description': 'Haora_18_2018', 'priority': 100, 'creation_timestamp_ms': 1762664525286, 'update_timestamp_ms': 1762664525286, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'KKHIYOLL6BV2VSDOLG2LTKTP', 'name': 'projects/ext-datasets/operations/KKHIYOLL6BV2VSDOLG2LTKTP'}\n","Grid 19\n","curr_year 2018\n","Saving data for Haora 2018\n","Task Started {'state': 'READY', 'description': 'Haora_19_2018', 'priority': 100, 'creation_timestamp_ms': 1762664536675, 'update_timestamp_ms': 1762664536675, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'JLT5QIEXY5TB2EBUKWLXSZYP', 'name': 'projects/ext-datasets/operations/JLT5QIEXY5TB2EBUKWLXSZYP'}\n","Grid 20\n","curr_year 2018\n","Saving data for Haora 2018\n","Task Started {'state': 'READY', 'description': 'Haora_20_2018', 'priority': 100, 'creation_timestamp_ms': 1762664544203, 'update_timestamp_ms': 1762664544203, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'FHJRHJSOOMUO6PMBUJS2TS4F', 'name': 'projects/ext-datasets/operations/FHJRHJSOOMUO6PMBUJS2TS4F'}\n","Grid 21\n","curr_year 2018\n","Saving data for Haora 2018\n","Task Started {'state': 'READY', 'description': 'Haora_21_2018', 'priority': 100, 'creation_timestamp_ms': 1762664552129, 'update_timestamp_ms': 1762664552129, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '224FAS3URVBHBNTR2DMQO7LZ', 'name': 'projects/ext-datasets/operations/224FAS3URVBHBNTR2DMQO7LZ'}\n","Grid 22\n","curr_year 2018\n","Saving data for Haora 2018\n","Task Started {'state': 'READY', 'description': 'Haora_22_2018', 'priority': 100, 'creation_timestamp_ms': 1762664559936, 'update_timestamp_ms': 1762664559936, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'F2O57WV3QQRWIR3FEDBM2OLS', 'name': 'projects/ext-datasets/operations/F2O57WV3QQRWIR3FEDBM2OLS'}\n","Grid 23\n","curr_year 2018\n","Saving data for Haora 2018\n","Task Started {'state': 'READY', 'description': 'Haora_23_2018', 'priority': 100, 'creation_timestamp_ms': 1762664567483, 'update_timestamp_ms': 1762664567483, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'NLSB5T3BA7WDJLPXRUWT7XCE', 'name': 'projects/ext-datasets/operations/NLSB5T3BA7WDJLPXRUWT7XCE'}\n","Grid 24\n","curr_year 2018\n","Saving data for Haora 2018\n","Task Started {'state': 'READY', 'description': 'Haora_24_2018', 'priority': 100, 'creation_timestamp_ms': 1762664575693, 'update_timestamp_ms': 1762664575693, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'RHY37T5Q2OQC6Q4K6WTPDKWY', 'name': 'projects/ext-datasets/operations/RHY37T5Q2OQC6Q4K6WTPDKWY'}\n","Grid 25\n","curr_year 2018\n","Saving data for Haora 2018\n","Task Started {'state': 'READY', 'description': 'Haora_25_2018', 'priority': 100, 'creation_timestamp_ms': 1762664584856, 'update_timestamp_ms': 1762664584856, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'ZYOYSH4L2WLKLGRUYDRO2IIL', 'name': 'projects/ext-datasets/operations/ZYOYSH4L2WLKLGRUYDRO2IIL'}\n","Grid 26\n","curr_year 2018\n","Saving data for Haora 2018\n","Task Started {'state': 'READY', 'description': 'Haora_26_2018', 'priority': 100, 'creation_timestamp_ms': 1762664593327, 'update_timestamp_ms': 1762664593327, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'XUSJK6T5NUNK33IWDEWIKVQS', 'name': 'projects/ext-datasets/operations/XUSJK6T5NUNK33IWDEWIKVQS'}\n","Grid 27\n","curr_year 2018\n","Saving data for Haora 2018\n","Task Started {'state': 'READY', 'description': 'Haora_27_2018', 'priority': 100, 'creation_timestamp_ms': 1762664601828, 'update_timestamp_ms': 1762664601828, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'ZS2KGFD5LLDWONYEZARCUXMF', 'name': 'projects/ext-datasets/operations/ZS2KGFD5LLDWONYEZARCUXMF'}\n","Grid 28\n","curr_year 2018\n","Saving data for Haora 2018\n","Task Started {'state': 'READY', 'description': 'Haora_28_2018', 'priority': 100, 'creation_timestamp_ms': 1762664609121, 'update_timestamp_ms': 1762664609121, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'ZDTE4SYNHNMOT7XJC6EUM7KI', 'name': 'projects/ext-datasets/operations/ZDTE4SYNHNMOT7XJC6EUM7KI'}\n","Grid 29\n","curr_year 2018\n","Saving data for Haora 2018\n","Task Started {'state': 'READY', 'description': 'Haora_29_2018', 'priority': 100, 'creation_timestamp_ms': 1762664618077, 'update_timestamp_ms': 1762664618077, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'KZOPAN6335LX4MQDYKBGBIOI', 'name': 'projects/ext-datasets/operations/KZOPAN6335LX4MQDYKBGBIOI'}\n","3379.8888437747955\n","Year 2018, District 17: Hugli, grids: 55\n","Grid 0\n","curr_year 2018\n","Saving data for Hugli 2018\n","Task Started {'state': 'READY', 'description': 'Hugli_0_2018', 'priority': 100, 'creation_timestamp_ms': 1762664632691, 'update_timestamp_ms': 1762664632691, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '4LSJGFY2HUKLUUD25DVIEWCH', 'name': 'projects/ext-datasets/operations/4LSJGFY2HUKLUUD25DVIEWCH'}\n","Grid 1\n","curr_year 2018\n","Saving data for Hugli 2018\n","Task Started {'state': 'READY', 'description': 'Hugli_1_2018', 'priority': 100, 'creation_timestamp_ms': 1762664641441, 'update_timestamp_ms': 1762664641441, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'A4WXJXOMDX4Y4U3JSNH7CAOP', 'name': 'projects/ext-datasets/operations/A4WXJXOMDX4Y4U3JSNH7CAOP'}\n","Grid 2\n","curr_year 2018\n","Saving data for Hugli 2018\n","Task Started {'state': 'READY', 'description': 'Hugli_2_2018', 'priority': 100, 'creation_timestamp_ms': 1762664648491, 'update_timestamp_ms': 1762664648491, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'QCOZ2SE6IBF23LRZOWNIK7I5', 'name': 'projects/ext-datasets/operations/QCOZ2SE6IBF23LRZOWNIK7I5'}\n","Grid 3\n","curr_year 2018\n","Saving data for Hugli 2018\n","Task Started {'state': 'READY', 'description': 'Hugli_3_2018', 'priority': 100, 'creation_timestamp_ms': 1762664655077, 'update_timestamp_ms': 1762664655077, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'QGY3SXOYPZJRM6CCJVD7N44O', 'name': 'projects/ext-datasets/operations/QGY3SXOYPZJRM6CCJVD7N44O'}\n","Grid 4\n","curr_year 2018\n","Saving data for Hugli 2018\n","Task Started {'state': 'READY', 'description': 'Hugli_4_2018', 'priority': 100, 'creation_timestamp_ms': 1762664658729, 'update_timestamp_ms': 1762664658729, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'FEG7VEPW6AEF4TBAUNISMCVN', 'name': 'projects/ext-datasets/operations/FEG7VEPW6AEF4TBAUNISMCVN'}\n","Grid 5\n","curr_year 2018\n","Saving data for Hugli 2018\n","Task Started {'state': 'READY', 'description': 'Hugli_5_2018', 'priority': 100, 'creation_timestamp_ms': 1762664667503, 'update_timestamp_ms': 1762664667503, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '6LFNZ5RUDRONF3QYFFTRTY2W', 'name': 'projects/ext-datasets/operations/6LFNZ5RUDRONF3QYFFTRTY2W'}\n","Grid 6\n","curr_year 2018\n","Saving data for Hugli 2018\n","Task Started {'state': 'READY', 'description': 'Hugli_6_2018', 'priority': 100, 'creation_timestamp_ms': 1762664676185, 'update_timestamp_ms': 1762664676185, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'O23VN4G5WXMZZGQ5NIRKZDNF', 'name': 'projects/ext-datasets/operations/O23VN4G5WXMZZGQ5NIRKZDNF'}\n","Grid 7\n","curr_year 2018\n","Saving data for Hugli 2018\n","Task Started {'state': 'READY', 'description': 'Hugli_7_2018', 'priority': 100, 'creation_timestamp_ms': 1762664684106, 'update_timestamp_ms': 1762664684106, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'AWFJ6TBAYZGDS4FGMAXOTC7I', 'name': 'projects/ext-datasets/operations/AWFJ6TBAYZGDS4FGMAXOTC7I'}\n","Grid 8\n","curr_year 2018\n","Saving data for Hugli 2018\n","Task Started {'state': 'READY', 'description': 'Hugli_8_2018', 'priority': 100, 'creation_timestamp_ms': 1762664690851, 'update_timestamp_ms': 1762664690851, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '5I3ID4OIFJKVQZMU3HUI3XJ5', 'name': 'projects/ext-datasets/operations/5I3ID4OIFJKVQZMU3HUI3XJ5'}\n","Grid 9\n","curr_year 2018\n","Saving data for Hugli 2018\n","Task Started {'state': 'READY', 'description': 'Hugli_9_2018', 'priority': 100, 'creation_timestamp_ms': 1762664695983, 'update_timestamp_ms': 1762664695983, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'XWFMYBV4GJQDNXQGETMD6PE6', 'name': 'projects/ext-datasets/operations/XWFMYBV4GJQDNXQGETMD6PE6'}\n","Grid 10\n","curr_year 2018\n","Saving data for Hugli 2018\n","Task Started {'state': 'READY', 'description': 'Hugli_10_2018', 'priority': 100, 'creation_timestamp_ms': 1762664701159, 'update_timestamp_ms': 1762664701159, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'TF7RAOWBKSVHM6VZ6DS6ZH2A', 'name': 'projects/ext-datasets/operations/TF7RAOWBKSVHM6VZ6DS6ZH2A'}\n","Grid 11\n","curr_year 2018\n","Saving data for Hugli 2018\n","Task Started {'state': 'READY', 'description': 'Hugli_11_2018', 'priority': 100, 'creation_timestamp_ms': 1762664711085, 'update_timestamp_ms': 1762664711085, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '55HGWTLODW7NRPMW4EFOJ643', 'name': 'projects/ext-datasets/operations/55HGWTLODW7NRPMW4EFOJ643'}\n","Grid 12\n","curr_year 2018\n","Saving data for Hugli 2018\n","Task Started {'state': 'READY', 'description': 'Hugli_12_2018', 'priority': 100, 'creation_timestamp_ms': 1762664718074, 'update_timestamp_ms': 1762664718074, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'XEYISSMQHOBBT45CSUTN5CC5', 'name': 'projects/ext-datasets/operations/XEYISSMQHOBBT45CSUTN5CC5'}\n","Grid 13\n","curr_year 2018\n","Saving data for Hugli 2018\n","Task Started {'state': 'READY', 'description': 'Hugli_13_2018', 'priority': 100, 'creation_timestamp_ms': 1762664723363, 'update_timestamp_ms': 1762664723363, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'XYDJYVBHE5MEXHJNFV6RI5D4', 'name': 'projects/ext-datasets/operations/XYDJYVBHE5MEXHJNFV6RI5D4'}\n","Grid 14\n","curr_year 2018\n","Saving data for Hugli 2018\n","Task Started {'state': 'READY', 'description': 'Hugli_14_2018', 'priority': 100, 'creation_timestamp_ms': 1762664730830, 'update_timestamp_ms': 1762664730830, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '3D3I4DWRJUNUFQ3KMFCRPR65', 'name': 'projects/ext-datasets/operations/3D3I4DWRJUNUFQ3KMFCRPR65'}\n","Grid 15\n","curr_year 2018\n","Saving data for Hugli 2018\n","Task Started {'state': 'READY', 'description': 'Hugli_15_2018', 'priority': 100, 'creation_timestamp_ms': 1762664735785, 'update_timestamp_ms': 1762664735785, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': '5YR6LDIBSLFGY2JJNJHTLOSY', 'name': 'projects/ext-datasets/operations/5YR6LDIBSLFGY2JJNJHTLOSY'}\n","Grid 16\n","curr_year 2018\n","Saving data for Hugli 2018\n","Task Started {'state': 'READY', 'description': 'Hugli_16_2018', 'priority': 100, 'creation_timestamp_ms': 1762664736650, 'update_timestamp_ms': 1762664736650, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'TFVTOFLDYMIPGB7HHLZ3JDZL', 'name': 'projects/ext-datasets/operations/TFVTOFLDYMIPGB7HHLZ3JDZL'}\n","Grid 17\n","curr_year 2018\n","Saving data for Hugli 2018\n","Task Started {'state': 'READY', 'description': 'Hugli_17_2018', 'priority': 100, 'creation_timestamp_ms': 1762664737289, 'update_timestamp_ms': 1762664737289, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'EYOMWWQPIQVBDRE4YDUWDWL3', 'name': 'projects/ext-datasets/operations/EYOMWWQPIQVBDRE4YDUWDWL3'}\n","Grid 18\n","curr_year 2018\n","Saving data for Hugli 2018\n","Task Started {'state': 'READY', 'description': 'Hugli_18_2018', 'priority': 100, 'creation_timestamp_ms': 1762664743037, 'update_timestamp_ms': 1762664743037, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'LQRI6YRY4TXUSC4GKF6LYOE6', 'name': 'projects/ext-datasets/operations/LQRI6YRY4TXUSC4GKF6LYOE6'}\n","Grid 19\n","curr_year 2018\n","Saving data for Hugli 2018\n","Task Started {'state': 'READY', 'description': 'Hugli_19_2018', 'priority': 100, 'creation_timestamp_ms': 1762664744354, 'update_timestamp_ms': 1762664744354, 'start_timestamp_ms': 0, 'task_type': 'EXPORT_FEATURES', 'id': 'ML45MRARB7WAQXAZWBJI43YD', 'name': 'projects/ext-datasets/operations/ML45MRARB7WAQXAZWBJI43YD'}\n","Grid 20\n","curr_year 2018\n"]}],"source":["# years = ['2016']\n","for curr_year in years:\n","\n"," total_time = 0\n","\n"," dist_cnt = 0\n"," for district in dist_list:\n","\n"," # if dist_cnt < 3:\n"," # dist_cnt += 1\n"," # continue\n","\n"," start_time = time.time()\n","\n"," district_aoi = india_district_boundary.filter(ee.Filter.eq('Name', district)).geometry()\n"," district_aoi = district_aoi.intersection(agrozone)\n"," features = createGrids(district_aoi)\n","\n"," print(f'Year {curr_year}, District {dist_cnt}: {district}, grids: {len(features)}')\n","\n"," path = f'drive/MyDrive/TreeHealth/{agroclimatic_zone}/{district}/{curr_year}/'\n"," os.makedirs(path, exist_ok=True)\n","\n"," df = pd.DataFrame()\n"," df['grid_num'] = list(range(len(features)))\n"," tree_points_list = []\n","\n"," i = 0\n"," for feature in features:\n"," print(f'Grid {i}')\n","\n"," coord = feature['geometry']['coordinates'][0]\n"," aoi = ee.Geometry.Polygon(coord)\n"," aoi = aoi.intersection(district_aoi)\n","\n"," img_name = district + \"_\" + str(i) + \"_\" + str(curr_year)\n","\n"," start_date = START_DATE[int(curr_year)]\n"," end_date = END_DATE[int(curr_year)]\n","\n"," # Get tree cover using both Dynamic World and IndiaSat (union)\n"," tree_cover = get_tree_cover(aoi, curr_year, scale=25)\n","\n"," season = 'kharif'\n"," try:\n"," image = get_s1_image(aoi, start_date[season], end_date[season])\n"," image = image.updateMask(tree_cover)\n"," except Exception as exp:\n"," print(\"S1 Error occured: \", season, exp)\n","\n"," try:\n"," s2_data = get_s2_image(aoi, start_date[season], end_date[season])\n"," s2_data = s2_data.updateMask(tree_cover)\n"," image = image.addBands(s2_data).select(s1_bands + s2_bands)\n"," image = image.rename([band + '_kharif' for band in s1_bands + s2_bands])\n"," except Exception as exp:\n"," print(\"S2 Error occured: \", season, exp)\n","\n"," for season in ['rabi', 'zaid']:\n"," try:\n"," s1_data = get_s1_image(aoi, start_date[season], end_date[season])\n"," s1_data = s1_data.updateMask(tree_cover)\n","\n"," except Exception as exp:\n"," print(\"S1 Error occured: \", season, exp)\n","\n"," try:\n"," s2_data = get_s2_image(aoi, start_date[season], end_date[season])\n"," s2_data = s2_data.updateMask(tree_cover)\n"," image_merged = s1_data.addBands(s2_data).select(s1_bands + s2_bands)\n"," image_merged = image_merged.rename([band + '_' + season for band in s1_bands + s2_bands])\n"," image = image.addBands(image_merged)\n","\n"," except Exception as exp:\n"," print(\"S2 Error occured: \", season, exp)\n","\n"," sample_points = image.sample(\n"," region = aoi,\n"," scale = 25,\n"," factor = 1,\n"," tileScale = 10,\n"," geometries = True\n"," )\n","\n"," sample_tree_cover = tree_cover.sample(\n"," region = aoi,\n"," scale = 25,\n"," factor = 1,\n"," tileScale = 10,\n"," geometries = True\n"," )\n","\n"," try:\n"," tree_points_list.append(sample_tree_cover.size().getInfo())\n"," except:\n"," tree_points_list.append(0)\n","\n"," try:\n"," task = save_data_csv(sample_points, img_name, district, curr_year)\n"," prev_task = task\n"," # tasks[curr_year][district] = task\n","\n"," except Exception as e:\n"," print(e)\n"," while prev_task.status()['state'] != 'COMPLETED' and prev_task.status()['state'] != 'FAILED':\n"," # time.sleep(10)\n"," continue\n"," task = save_data_csv(sample_points, img_name, path, curr_year)\n"," # tasks[curr_year][district] = task\n"," prev_task = task\n","\n"," i += 1\n","\n"," df['tree_points'] = tree_points_list\n"," df.to_csv(path + 'tree_cover_points.csv', index=False)\n","\n"," dist_cnt += 1\n"," end_time = time.time()\n","\n"," total_time += (end_time - start_time)\n"," print(total_time)\n","\n"," print(\"Waiting for last task to be completed...\")\n"," while prev_task.status()['state'] != 'COMPLETED' and prev_task.status()['state'] != 'FAILED':\n"," continue\n"," print(\"Last task completed!\")\n","\n"," total_time += (time.time() - end_time)\n"," print(\"Total Time Taken:\", total_time)"]}],"metadata":{"colab":{"collapsed_sections":["snxgyeoRno-K"],"provenance":[{"file_id":"10rsYxkf8VtyBQYo6nDSxJeQg-Kr0OKDh","timestamp":1758123472823}]},"kernelspec":{"display_name":"Python 3","name":"python3"},"language_info":{"name":"python"}},"nbformat":4,"nbformat_minor":0} \ No newline at end of file diff --git a/utilities/scripts/tree_health/colab_notebooks/predict_ccd_ch_results.ipynb b/utilities/scripts/tree_health/colab_notebooks/predict_ccd_ch_results.ipynb new file mode 100644 index 00000000..3b0c650b --- /dev/null +++ b/utilities/scripts/tree_health/colab_notebooks/predict_ccd_ch_results.ipynb @@ -0,0 +1 @@ +{"cells":[{"cell_type":"code","execution_count":1,"metadata":{"executionInfo":{"elapsed":3189,"status":"ok","timestamp":1775402326200,"user":{"displayName":"CoRE Stack","userId":"15795073488475210764"},"user_tz":-330},"id":"kMqXI9xEx9Ij"},"outputs":[],"source":["\n","import pandas as pd\n","from glob import glob\n","import joblib\n","import json\n","import time\n","from copy import deepcopy\n","import os\n","import re\n","import numpy as np\n","import ee\n","import ast"]},{"cell_type":"code","execution_count":null,"metadata":{"colab":{"base_uri":"https://localhost:8080/"},"executionInfo":{"elapsed":10,"status":"ok","timestamp":1771429377492,"user":{"displayName":"CoRE Stack","userId":"15795073488475210764"},"user_tz":-330},"id":"EX11mwSCcEls","outputId":"03f4e89d-5d52-4780-b0a8-6f7bf4864843"},"outputs":[{"name":"stdout","output_type":"stream","text":["Drive not mounted, so nothing to flush and unmount.\n"]}],"source":["# from google.colab import drive\n","# drive.flush_and_unmount()"]},{"cell_type":"code","execution_count":2,"metadata":{"colab":{"base_uri":"https://localhost:8080/"},"collapsed":true,"executionInfo":{"elapsed":22349,"status":"ok","timestamp":1775402348561,"user":{"displayName":"CoRE Stack","userId":"15795073488475210764"},"user_tz":-330},"id":"3AzHO9S6xtlx","outputId":"dc759937-64e1-4993-b5ea-30d480ae29d2"},"outputs":[{"output_type":"stream","name":"stdout","text":["Mounted at /content/drive\n"]}],"source":["from google.colab import drive\n","drive.mount('/content/drive')"]},{"cell_type":"code","execution_count":3,"metadata":{"executionInfo":{"elapsed":1,"status":"ok","timestamp":1775402348576,"user":{"displayName":"CoRE Stack","userId":"15795073488475210764"},"user_tz":-330},"id":"6v5AhpfqyCIX"},"outputs":[],"source":["agroclimaticZone_acronym_dict = {'Eastern Plateau & Hills Region': 'EPAHR',\n"," 'Southern Plateau and Hills Region': 'SPAHR',\n"," 'East Coast Plains & Hills Region': 'ECPHR',\n"," 'Western Plateau and Hills Region': 'WPAHR',\n"," 'Central Plateau & Hills Region': 'CPAHR',\n"," 'Lower Gangetic Plain Region': 'LGPR',\n"," 'Middle Gangetic Plain Region': 'MGPR',\n"," 'Eastern Himalayan Region': 'EHR',\n"," 'Western Himalayan Region': 'WHR',\n"," 'Upper Gangetic Plain Region': 'UGPR',\n"," 'Trans Gangetic Plain Region': 'TGPR',\n"," 'West Coast Plains & Ghat Region': 'WCPGR',\n"," 'Gujarat Plains & Hills Region': 'GPHR',\n"," 'Western Dry Region': 'WDR'}"]},{"cell_type":"code","execution_count":4,"metadata":{"executionInfo":{"elapsed":2,"status":"ok","timestamp":1775402354345,"user":{"displayName":"CoRE Stack","userId":"15795073488475210764"},"user_tz":-330},"id":"27BoVlCPyGq5"},"outputs":[],"source":["best_month_dict = {'Eastern Plateau & Hills Region': 'cc_12',\n"," 'Middle Gangetic Plain Region': 'cc_10',\n"," 'Lower Gangetic Plain Region': 'cc_9',\n"," 'Western Himalayan Region': 'cc_8',\n"," 'Eastern Himalayan Region': 'cc_10',\n"," 'Upper Gangetic Plain Region': 'cc_9',\n"," 'Trans Gangetic Plain Region': 'cc_9',\n"," 'Central Plateau & Hills Region': 'cc_7',\n"," 'Western Plateau and Hills Region': 'cc_11',\n"," 'Southern Plateau and Hills Region': 'cc_8',\n"," 'East Coast Plains & Hills Region': 'cc_12'}\n"]},{"cell_type":"code","execution_count":5,"metadata":{"executionInfo":{"elapsed":2,"status":"ok","timestamp":1775402361003,"user":{"displayName":"CoRE Stack","userId":"15795073488475210764"},"user_tz":-330},"id":"z9qLXf0PyK7B"},"outputs":[],"source":["# agroclimatic_zone = 'Eastern Plateau & Hills Region'\n","agroclimatic_zone = 'Southern Plateau and Hills Region'\n","# agroclimatic_zone = \"East Coast Plains & Hills Region\"\n","# agroclimatic_zone = 'Western Plateau and Hills Region'\n","# agroclimatic_zone = \"Central Plateau & Hills Region\"\n","# agroclimatic_zone = 'Lower Gangetic Plain Region'\n","# agroclimatic_zone = 'Middle Gangetic Plain Region'\n","# agroclimatic_zone = 'Eastern Himalayan Region'\n","# agroclimatic_zone = 'Western Himalayan Region'\n","# agroclimatic_zone = 'Trans Gangetic Plain Region'\n","# agroclimatic_zone = \"Upper Gangetic Plain Region\"\n","# agroclimatic_zone = \"West Coast Plains & Ghat Region\" # Model not available\n","# agroclimatic_zone = 'Gujarat Plains & Hills Region' # Model not available\n","# agroclimatic_zone = 'Western Dry Region' # Model not available"]},{"cell_type":"code","execution_count":6,"metadata":{"executionInfo":{"elapsed":3,"status":"ok","timestamp":1775402369694,"user":{"displayName":"CoRE Stack","userId":"15795073488475210764"},"user_tz":-330},"id":"LNPicKBZyQfD"},"outputs":[],"source":["# Set the list of years for which to rename folders\n","years = ['2017']# ['2016', '2017', '2018','2019','2020','2021','2022','2023','2024']"]},{"cell_type":"markdown","metadata":{"id":"P_Php5yrbPm8"},"source":["# CHM"]},{"cell_type":"code","execution_count":8,"metadata":{"colab":{"base_uri":"https://localhost:8080/"},"executionInfo":{"elapsed":13,"status":"ok","timestamp":1775402390205,"user":{"displayName":"CoRE Stack","userId":"15795073488475210764"},"user_tz":-330},"id":"-m5EML9zyVX4","outputId":"444a0e1c-35ef-4049-9799-7eba9924b2f6"},"outputs":[{"output_type":"stream","name":"stdout","text":["['Anantapur']\n"]}],"source":["df = pd.read_csv(f'/content/drive/MyDrive/TreeHealth/Agroclimatic_regions/{agroclimatic_zone}.csv')\n","dist_list = list(df['Name'])\n","dist_list = ['Anantapur']\n","print(dist_list)"]},{"cell_type":"code","execution_count":9,"metadata":{"executionInfo":{"elapsed":8,"status":"ok","timestamp":1775402392754,"user":{"displayName":"CoRE Stack","userId":"15795073488475210764"},"user_tz":-330},"id":"tMOnWtVNygfj"},"outputs":[],"source":["# Set the root directory to the specified folder\n","root_dir = '/content/drive/MyDrive/'\n","os.chdir(root_dir)"]},{"cell_type":"code","execution_count":10,"metadata":{"colab":{"base_uri":"https://localhost:8080/"},"executionInfo":{"elapsed":108,"status":"ok","timestamp":1775402395585,"user":{"displayName":"CoRE Stack","userId":"15795073488475210764"},"user_tz":-330},"id":"ybIqOkpbykty","outputId":"76b07e97-7cca-4407-a346-174ea2639b37"},"outputs":[{"output_type":"stream","name":"stdout","text":["/content/drive/MyDrive\n"]}],"source":["! pwd"]},{"cell_type":"code","execution_count":11,"metadata":{"colab":{"base_uri":"https://localhost:8080/"},"collapsed":true,"executionInfo":{"elapsed":28,"status":"ok","timestamp":1775402397122,"user":{"displayName":"CoRE Stack","userId":"15795073488475210764"},"user_tz":-330},"id":"iiwNIME5yo3A","outputId":"9d48c10e-ba93-4a7b-eb20-3633974cfd7f"},"outputs":[{"output_type":"stream","name":"stdout","text":["core-stack-docs\n","kapil_test\n","Colab Notebooks\n","Pan_india_maps_screenshots\n","ATCF_ch_5jun25\n","Google Earth\n","Datasets\n","WBC_data\n","Apache\n","tiff_export\n","Pan_india\n","Aquifer_vector\n","Aquifer_export\n","TreeHealth\n","MGPR\n","pennar_lulc_23_24\n","LGPR1\n","lcw\n","Untitled form (File responses)\n","change_test\n","ET_downloads\n","core_stack_manual\n","WHR_2024\n","WHR_2023\n","WHR_2022\n","WHR_2021\n","WHR_2020\n","WHR_2019\n","WHR_2018\n","WHR_2017\n","WHR_2016\n","EPAHR_2016\n","EPAHR_2017\n","EPAHR_2018\n","EPAHR_2019\n","EPAHR_2020\n","EPAHR_2021\n","EPAHR_2022\n","EPAHR_2023\n","SPAHR_2016\n","SPAHR_2017\n","SPAHR_2018\n","SPAHR_2019\n","SPAHR_2020\n","SPAHR_2021\n","SPAHR_2022\n","SPAHR_2023\n","ECPHR_2016\n","ECPHR_2017\n","ECPHR_2018\n","ECPHR_2019\n","ECPHR_2020\n","ECPHR_2021\n","ECPHR_2022\n","ECPHR_2023\n","WPAHR_2016\n","WPAHR_2017\n","WPAHR_2018\n","WPAHR_2019\n","WPAHR_2020\n","WPAHR_2021\n","WPAHR_2022\n","WPAHR_2023\n","CPAHR_2016\n","CPAHR_2017\n","CPAHR_2018\n","CPAHR_2019\n","CPAHR_2020\n","CPAHR_2021\n","CPAHR_2022\n","CPAHR_2023\n","LGPR_2016\n","MGPR_2016\n","EHR_2016\n","EHR_2017\n","EHR_2018\n","EHR_2019\n","EHR_2020\n","EHR_2021\n","EHR_2022\n","EHR_2023\n","UGPR_2016\n","UGPR_2017\n","UGPR_2018\n","UGPR_2019\n","UGPR_2020\n","UGPR_2021\n","UGPR_2022\n","UGPR_2023\n","TGPR_2016\n","TGPR_2017\n","TGPR_2018\n","TGPR_2019\n","TGPR_2020\n","TGPR_2021\n","TGPR_2022\n","TGPR_2023\n","WCPGR_2016\n","WCPGR_2017\n","WCPGR_2018\n","WCPGR_2019\n","WCPGR_2020\n","WCPGR_2021\n","WCPGR_2022\n","WCPGR_2023\n","GPHR_2016\n","GPHR_2017\n","GPHR_2018\n","GPHR_2019\n","GPHR_2020\n","GPHR_2021\n","GPHR_2022\n","GPHR_2023\n","WDR_2016\n","WDR_2017\n","WDR_2018\n","WDR_2019\n","WDR_2020\n","WDR_2021\n","WDR_2022\n","WDR_2023\n","LGPR_2017\n","LGPR_2018\n","LGPR_2019\n","LGPR_2020\n","LGPR_2021\n","LGPR_2022\n","LGPR_2023\n","EHR_2024\n","WDR_2024\n","UGPR_2024\n","hydrological_boundaries\n","TGPR_2024\n","MGPR_2024\n","LGPR_2024\n","EPAHR_2024\n","SPAHR_2024\n","ECPHR_2024\n","WPAHR_2024\n","CPAHR_2024\n","MGPR_2017\n","MGPR_2018\n","MGPR_2019\n","MGPR_2020\n","MGPR_2021\n","MGPR_2022\n","MGPR_2023\n","mws_tmp\n","FOSS - CoRE Stack Material\n","wassan_boundaries\n","demand validtor\n"]}],"source":["# Get the current directory\n","current_directory = os.getcwd()\n","\n","# List all folders in the current directory\n","folders = [f for f in os.listdir(current_directory) if os.path.isdir(os.path.join(current_directory, f))]\n","\n","# Print the list of folders\n","# print(\"Folders in the current directory:\")\n","\n","for folder in folders:\n"," print(folder)"]},{"cell_type":"code","execution_count":12,"metadata":{"colab":{"base_uri":"https://localhost:8080/"},"collapsed":true,"executionInfo":{"elapsed":7,"status":"ok","timestamp":1775402400124,"user":{"displayName":"CoRE Stack","userId":"15795073488475210764"},"user_tz":-330},"id":"VeRQDIsyysC-","outputId":"c9b23c9f-78c3-4baa-9bae-44dde8cb023c"},"outputs":[{"output_type":"stream","name":"stdout","text":["Folders starting with 'SPAHR':\n","SPAHR_2016\n","SPAHR_2017\n","SPAHR_2018\n","SPAHR_2019\n","SPAHR_2020\n","SPAHR_2021\n","SPAHR_2022\n","SPAHR_2023\n","SPAHR_2024\n"]}],"source":["# Regular expression pattern to match folder names starting with a particular pattern\n","pattern = re.compile(r'^' + f'{agroclimaticZone_acronym_dict[agroclimatic_zone]}')\n","\n","# Filter folders based on the pattern\n","matching_folders = [folder for folder in folders if pattern.match(folder)]\n","\n","# Print the matching folders\n","print(f\"Folders starting with '{agroclimaticZone_acronym_dict[agroclimatic_zone]}':\")\n","for folder in matching_folders:\n"," print(folder)"]},{"cell_type":"code","execution_count":13,"metadata":{"colab":{"base_uri":"https://localhost:8080/"},"collapsed":true,"executionInfo":{"elapsed":11,"status":"ok","timestamp":1775402403714,"user":{"displayName":"CoRE Stack","userId":"15795073488475210764"},"user_tz":-330},"id":"KxX7a9qMywMW","outputId":"a0873f64-5add-4fde-9974-66029b019278"},"outputs":[{"output_type":"stream","name":"stdout","text":["2017\n","district_year_folders--> ['SPAHR_2017']\n"]}],"source":["# Leh (Ladkh) is the only district encountered having special character '(' and ')' in it. That's why its handled in a special way as seen here\n","\n","# Transfer files from duplicate folders to original folder\n","\n","dist_num = 0\n","# for district in dist_list:\n","# print(dist_num)\n","for year in years:\n"," print(year)\n","\n"," # orig_district = district\n"," # if district == 'Leh (Ladakh)':\n"," # district = 'Leh'\n","\n"," # pattern = re.compile(r'^' + agroclimaticZone_acronym_dict[agroclimatic_zone] + '_' + district + '_' + year)\n"," pattern = re.compile(r'^' + agroclimaticZone_acronym_dict[agroclimatic_zone] + '_' + year)\n"," district_year_folders = [folder for folder in matching_folders if pattern.match(folder)]\n"," print(\"district_year_folders-->\", district_year_folders)\n","\n"," # district = orig_district\n","\n"," while len(district_year_folders) > 1:\n"," source_folder = district_year_folders[0]\n"," destination_folder = district_year_folders[1]\n","\n"," files_to_move = os.listdir(source_folder)\n"," for file_name in files_to_move:\n"," source_path = os.path.join(source_folder, file_name)\n"," destination_path = os.path.join(destination_folder, file_name)\n"," os.rename(source_path, destination_path)\n","\n"," del district_year_folders[0]\n"," if len(district_year_folders) > 0:\n"," current_folder_name = district_year_folders[0]\n"," # new_folder_name = f'{agroclimaticZone_acronym_dict[agroclimatic_zone]}_{district}_{year}'\n"," new_folder_name = f'{agroclimaticZone_acronym_dict[agroclimatic_zone]}_{year}'\n"," os.rename(current_folder_name, new_folder_name)\n","\n"," # dist_num += 1"]},{"cell_type":"code","execution_count":14,"metadata":{"colab":{"base_uri":"https://localhost:8080/"},"collapsed":true,"executionInfo":{"elapsed":5,"status":"ok","timestamp":1775402407191,"user":{"displayName":"CoRE Stack","userId":"15795073488475210764"},"user_tz":-330},"id":"VjlSG5iPQkyP","outputId":"c10595ee-e3dd-47b0-dae2-9ec79f181cfd"},"outputs":[{"output_type":"stream","name":"stdout","text":["SPAHR_2017\n"]}],"source":["# Check all folders with more than 0 files\n","dist_num = 0\n","# for district in dist_list:\n","# print(dist_num)\n","for year in years:\n"," # orig_district = district\n"," # if district == 'Leh (Ladakh)':\n"," # district = 'Leh'\n","\n"," # Re-scan the current directory for folders after renaming\n"," current_directory = os.getcwd()\n"," folders = [f for f in os.listdir(current_directory) if os.path.isdir(os.path.join(current_directory, f))]\n"," # pattern = re.compile(r'^' + agroclimaticZone_acronym_dict[agroclimatic_zone] + '_' + district + '_' + year)\n"," pattern = re.compile(r'^' + agroclimaticZone_acronym_dict[agroclimatic_zone] + '_' + year)\n"," district_year_folders = [folder for folder in folders if pattern.match(folder)]\n","\n"," # district = orig_district\n","\n"," for folder in district_year_folders:\n"," if len(os.listdir(folder)) > 0:\n"," print(folder)\n","\n"," # dist_num += 1"]},{"cell_type":"code","execution_count":15,"metadata":{"executionInfo":{"elapsed":9,"status":"ok","timestamp":1775402410054,"user":{"displayName":"CoRE Stack","userId":"15795073488475210764"},"user_tz":-330},"id":"aFKnyEbHRJku"},"outputs":[],"source":["# Change back to parent directory\n","os.chdir(os.path.dirname(os.getcwd()))"]},{"cell_type":"code","execution_count":16,"metadata":{"colab":{"base_uri":"https://localhost:8080/"},"executionInfo":{"elapsed":15,"status":"ok","timestamp":1775402411295,"user":{"displayName":"CoRE Stack","userId":"15795073488475210764"},"user_tz":-330},"id":"dnodhGcxRbTn","outputId":"718cbfc3-4389-437a-dd2c-cdbf4d80c238"},"outputs":[{"output_type":"stream","name":"stdout","text":["/content/drive\n"]}],"source":["! pwd"]},{"cell_type":"code","execution_count":17,"metadata":{"colab":{"base_uri":"https://localhost:8080/"},"executionInfo":{"elapsed":409,"status":"ok","timestamp":1775402413224,"user":{"displayName":"CoRE Stack","userId":"15795073488475210764"},"user_tz":-330},"id":"FPdn0lZ5bYDc","outputId":"c2b874d4-4a66-4752-a84d-570a5f6817ed"},"outputs":[{"output_type":"stream","name":"stdout","text":["666\n"]}],"source":["df = pd.read_csv('/content/drive/MyDrive/TreeHealth/district_to_agroclimaticZone_mapping.csv')\n","print(df.shape[0])"]},{"cell_type":"code","execution_count":18,"metadata":{"colab":{"base_uri":"https://localhost:8080/"},"collapsed":true,"executionInfo":{"elapsed":7,"status":"ok","timestamp":1775402414562,"user":{"displayName":"CoRE Stack","userId":"15795073488475210764"},"user_tz":-330},"id":"LvDi-4okc42F","outputId":"5b51c09f-7082-40a8-d336-bb9db8948a24"},"outputs":[{"output_type":"stream","name":"stdout","text":[" District \\\n","0 Nicobar Islands \n","1 North and Middle Andaman \n","2 South Andaman \n","3 Anantapur \n","4 Chittoor \n",".. ... \n","661 Pashchim Medinipur \n","662 Purba Medinipur \n","663 Puruliya \n","664 South 24 Parganas \n","665 Uttar Dinajpur \n","\n"," IntersectingZones \\\n","0 [] \n","1 [] \n","2 [] \n","3 [Southern Plateau and Hills Region] \n","4 [Southern Plateau and Hills Region, East Coast... \n",".. ... \n","661 [East Coast Plains & Hills Region, Eastern Pla... \n","662 [East Coast Plains & Hills Region, Lower Gange... \n","663 [Eastern Plateau & Hills Region, Lower Gangeti... \n","664 [Lower Gangetic Plain Region] \n","665 [Middle Gangetic Plain Region, Lower Gangetic ... \n","\n"," IntersectingAreas \\\n","0 [] \n","1 [] \n","2 [] \n","3 [19296696352.156364] \n","4 [11507580448.526262, 3718273353.5141] \n",".. ... \n","661 [27781135.047970004, 88721717.34314527, 930766... \n","662 [10359493.555347942, 3981478759.978645] \n","663 [6266123538.885962, 5557396.356690076] \n","664 [5541282840.37764] \n","665 [73899445.66614598, 3263671615.4575567, 414168... \n","\n"," AgroclimaticZone \n","0 NaN \n","1 NaN \n","2 NaN \n","3 Southern Plateau and Hills Region \n","4 Southern Plateau and Hills Region \n",".. ... \n","661 Lower Gangetic Plain Region \n","662 Lower Gangetic Plain Region \n","663 Eastern Plateau & Hills Region \n","664 Lower Gangetic Plain Region \n","665 Lower Gangetic Plain Region \n","\n","[666 rows x 4 columns]\n"]}],"source":["# Function to convert string representation of list to an actual list\n","def convert_to_list(string):\n"," return ast.literal_eval(string)\n","\n","df['IntersectingZones'] = df['IntersectingZones'].apply(convert_to_list)\n","print(df)"]},{"cell_type":"code","execution_count":19,"metadata":{"colab":{"base_uri":"https://localhost:8080/"},"collapsed":true,"executionInfo":{"elapsed":16,"status":"ok","timestamp":1775402418359,"user":{"displayName":"CoRE Stack","userId":"15795073488475210764"},"user_tz":-330},"id":"ghNTHMVrc7bV","outputId":"0e6921a1-078b-458b-986b-9878e5c93da8"},"outputs":[{"output_type":"stream","name":"stdout","text":[" District IntersectingZones\n","3 Anantapur [Southern Plateau and Hills Region]\n"]}],"source":["district_mapping_df = df[df['AgroclimaticZone'] == agroclimatic_zone][['District', 'IntersectingZones']]\n","district_mapping_df= district_mapping_df[district_mapping_df['District'].isin(dist_list)] ## Added extra\n","print(district_mapping_df)"]},{"cell_type":"code","execution_count":20,"metadata":{"colab":{"base_uri":"https://localhost:8080/"},"collapsed":true,"executionInfo":{"elapsed":18,"status":"ok","timestamp":1775402420291,"user":{"displayName":"CoRE Stack","userId":"15795073488475210764"},"user_tz":-330},"id":"9LAuCvPWdAmC","outputId":"defbc02c-e8a6-4a3b-d209-3e585b601b6c"},"outputs":[{"output_type":"stream","name":"stdout","text":["0 Anantapur ['Southern Plateau and Hills Region']\n"]}],"source":["\n","# print(district_mapping_df['IntersectingZones'][165])\n","# district_mapping_df['IntersectingZones']\n","i = 0\n","for ind in district_mapping_df.index:\n"," district = district_mapping_df.loc[ind, 'District']\n"," zones = district_mapping_df['IntersectingZones'][ind]\n"," print(i, district, zones)\n"," i += 1\n"," # for zone in zones:\n"," # if zone not in agroclimaticZone_acronym_dict:\n"," # print(district, zone)"]},{"cell_type":"code","execution_count":21,"metadata":{"executionInfo":{"elapsed":20,"status":"ok","timestamp":1775402421855,"user":{"displayName":"CoRE Stack","userId":"15795073488475210764"},"user_tz":-330},"id":"bE9YcsyVdMGK"},"outputs":[],"source":["agroclimatic_zone_model_path_mapping_rh98 = {'Eastern Plateau & Hills Region': '/content/drive/MyDrive/TreeHealth/best_models/chm_final/Eastern_Plateau_Hills_Region_correct_toa_rh98_24.joblib',\n"," 'East Coast Plains & Hills Region': '/content/drive/MyDrive/TreeHealth/best_models/chm_final/East_Coast_Plains_Hills_Region_correct_toa_rh98_23.joblib',\n"," 'Western Himalayan Region': '/content/drive/MyDrive/TreeHealth/best_models/chm_final/Western_Himalayan_Region_correct_toa_rh98_30.joblib',\n"," 'Eastern Himalayan Region': '/content/drive/MyDrive/TreeHealth/best_models/chm_final/Eastern_Himalayan_Region_correct_toa_rh98_25.joblib',\n"," 'Central Plateau & Hills Region': '/content/drive/MyDrive/TreeHealth/best_models/chm_final/Central_Plateau_Hills_Region_correct_toa_rh98_23.joblib',\n"," 'Southern Plateau and Hills Region': '/content/drive/MyDrive/TreeHealth/best_models/chm_final/Southern_Plateau_and_Hills_Region_correct_toa_rh98_23.joblib',\n"," 'Western Plateau and Hills Region': '/content/drive/MyDrive/TreeHealth/best_models/chm_final/Western_Plateau_and_Hills_Region_correct_toa_rh98_24.joblib',\n"," 'Upper Gangetic Plain Region': '/content/drive/MyDrive/TreeHealth/best_models/chm_final/Upper_Gangetic_Plain_Region_correct_toa_rh98_29.joblib',\n"," 'Middle Gangetic Plain Region': '/content/drive/MyDrive/TreeHealth/best_models/chm_final/Middle_Gangetic_Plain_Region_correct_toa_rh98_24.joblib',\n"," 'Trans Gangetic Plain Region': '/content/drive/MyDrive/TreeHealth/best_models/chm_final/Trans_Gangetic_Plain_Region_correct_toa_rh98_21.joblib',\n"," 'Lower Gangetic Plain Region': '/content/drive/MyDrive/TreeHealth/best_models/chm_final/Lower_Gangetic_Plain_Region_correct_toa_rh98_17.joblib'}\n","\n","agroclimatic_zone_model_path_mapping_rh75 = {'Eastern Plateau & Hills Region': '/content/drive/MyDrive/TreeHealth/best_models/chm_final/Eastern_Plateau_Hills_Region_correct_toa_rh75_17.joblib',\n"," 'East Coast Plains & Hills Region': '/content/drive/MyDrive/TreeHealth/best_models/chm_final/East_Coast_Plains_Hills_Region_correct_toa_rh75_16.joblib',\n"," 'Western Himalayan Region': '/content/drive/MyDrive/TreeHealth/best_models/chm_final/Western_Himalayan_Region_correct_toa_rh75_20.joblib',\n"," 'Eastern Himalayan Region': '/content/drive/MyDrive/TreeHealth/best_models/chm_final/Eastern_Himalayan_Region_correct_toa_rh75_18.joblib',\n"," 'Central Plateau & Hills Region': '/content/drive/MyDrive/TreeHealth/best_models/chm_final/Central_Plateau_Hills_Region_correct_toa_rh75_16.joblib',\n"," 'Southern Plateau and Hills Region': '/content/drive/MyDrive/TreeHealth/best_models/chm_final/Southern_Plateau_and_Hills_Region_correct_toa_rh75_16.joblib',\n"," 'Western Plateau and Hills Region': '/content/drive/MyDrive/TreeHealth/best_models/chm_final/Western_Plateau_and_Hills_Region_correct_toa_rh75_17.joblib',\n"," 'Upper Gangetic Plain Region': '/content/drive/MyDrive/TreeHealth/best_models/chm_final/Upper_Gangetic_Plain_Region_correct_toa_rh75_22.joblib',\n"," 'Middle Gangetic Plain Region': '/content/drive/MyDrive/TreeHealth/best_models/chm_final/Middle_Gangetic_Plain_Region_correct_toa_rh75_17.joblib',\n"," 'Trans Gangetic Plain Region': '/content/drive/MyDrive/TreeHealth/best_models/chm_final/Trans_Gangetic_Plain_Region_correct_toa_rh75_15.joblib',\n"," 'Lower Gangetic Plain Region': '/content/drive/MyDrive/TreeHealth/best_models/chm_final/Lower_Gangetic_Plain_Region_correct_toa_rh75_12.joblib'}\n","\n","agroclimatic_zone_model_path_mapping_rh50 = {'Eastern Plateau & Hills Region': '/content/drive/MyDrive/TreeHealth/best_models/chm_final/Eastern_Plateau_Hills_Region_correct_toa_rh50_12.joblib',\n"," 'East Coast Plains & Hills Region': '/content/drive/MyDrive/TreeHealth/best_models/chm_final/East_Coast_Plains_Hills_Region_correct_toa_rh50_12.joblib',\n"," 'Western Himalayan Region': '/content/drive/MyDrive/TreeHealth/best_models/chm_final/Western_Himalayan_Region_correct_toa_rh50_14.joblib',\n"," 'Eastern Himalayan Region': '/content/drive/MyDrive/TreeHealth/best_models/chm_final/Eastern_Himalayan_Region_correct_toa_rh50_13.joblib',\n"," 'Central Plateau & Hills Region': '/content/drive/MyDrive/TreeHealth/best_models/chm_final/Central_Plateau_Hills_Region_correct_toa_rh50_11.joblib',\n"," 'Southern Plateau and Hills Region': '/content/drive/MyDrive/TreeHealth/best_models/chm_final/Southern_Plateau_and_Hills_Region_correct_toa_rh50_12.joblib',\n"," 'Western Plateau and Hills Region': '/content/drive/MyDrive/TreeHealth/best_models/chm_final/Western_Plateau_and_Hills_Region_correct_toa_rh50_12.joblib',\n"," 'Upper Gangetic Plain Region': '/content/drive/MyDrive/TreeHealth/best_models/chm_final/Upper_Gangetic_Plain_Region_correct_toa_rh50_17.joblib',\n"," 'Middle Gangetic Plain Region': '/content/drive/MyDrive/TreeHealth/best_models/chm_final/Middle_Gangetic_Plain_Region_correct_toa_rh50_12.joblib',\n"," 'Trans Gangetic Plain Region': '/content/drive/MyDrive/TreeHealth/best_models/chm_final/Trans_Gangetic_Plain_Region_correct_toa_rh50_11.joblib',\n"," 'Lower Gangetic Plain Region': '/content/drive/MyDrive/TreeHealth/best_models/chm_final/Lower_Gangetic_Plain_Region_correct_toa_rh50_9.joblib'}"]},{"cell_type":"code","execution_count":22,"metadata":{"collapsed":true,"executionInfo":{"elapsed":4349,"status":"ok","timestamp":1775402428454,"user":{"displayName":"CoRE Stack","userId":"15795073488475210764"},"user_tz":-330},"id":"NFPKdyt3dQao","colab":{"base_uri":"https://localhost:8080/"},"outputId":"2e35db01-1b8c-4c2a-a44d-c4b6abaa79ce"},"outputs":[{"output_type":"stream","name":"stderr","text":["/usr/lib/python3.12/pickle.py:1760: UserWarning: [15:20:26] WARNING: /__w/xgboost/xgboost/src/collective/../data/../common/error_msg.h:83: If you are loading a serialized model (like pickle in Python, RDS in R) or\n","configuration generated by an older version of XGBoost, please export the model by calling\n","`Booster.save_model` from that version first, then load it back in current version. See:\n","\n"," https://xgboost.readthedocs.io/en/stable/tutorials/saving_model.html\n","\n","for more details about differences between saving model and serializing.\n","\n"," setstate(state)\n"]}],"source":["MODEL_PATH_rh98 = agroclimatic_zone_model_path_mapping_rh98[agroclimatic_zone]\n","model_rh98 = joblib.load(MODEL_PATH_rh98)\n","\n","MODEL_PATH_rh75 = agroclimatic_zone_model_path_mapping_rh75[agroclimatic_zone]\n","model_rh75 = joblib.load(MODEL_PATH_rh75)\n","\n","MODEL_PATH_rh50 = agroclimatic_zone_model_path_mapping_rh50[agroclimatic_zone]\n","model_rh50 = joblib.load(MODEL_PATH_rh50)"]},{"cell_type":"code","execution_count":23,"metadata":{"executionInfo":{"elapsed":5,"status":"ok","timestamp":1775402428461,"user":{"displayName":"CoRE Stack","userId":"15795073488475210764"},"user_tz":-330},"id":"43wGQ6lvfkRR"},"outputs":[],"source":["if hasattr(model_rh98, 'feature_names_in_'):\n"," features_rh98 = model_rh98.feature_names_in_\n","\n","if hasattr(model_rh75, 'feature_names_in_'):\n"," features_rh75 = model_rh75.feature_names_in_\n","\n","if hasattr(model_rh50, 'feature_names_in_'):\n"," features_rh50 = model_rh50.feature_names_in_\n"]},{"cell_type":"code","execution_count":24,"metadata":{"executionInfo":{"elapsed":9,"status":"ok","timestamp":1775402429003,"user":{"displayName":"CoRE Stack","userId":"15795073488475210764"},"user_tz":-330},"id":"YzC0_-8hfnB4"},"outputs":[],"source":["seasons = ['kharif', 'rabi', 'zaid']"]},{"cell_type":"code","execution_count":25,"metadata":{"executionInfo":{"elapsed":3,"status":"ok","timestamp":1775402430860,"user":{"displayName":"CoRE Stack","userId":"15795073488475210764"},"user_tz":-330},"id":"C2X1usS0fqgK"},"outputs":[],"source":["def add_s1_indices(df):\n"," for season in seasons:\n"," # SAR Indices\n"," df[f'VV_VH_Ratio_{season}'] = df[f'VV_{season}'] / df[f'VH_{season}']\n"," df[f'VH_VV_Ratio_{season}'] = df[f'VH_{season}'] / df[f'VV_{season}']\n"," df[f'SAR_NDVI_{season}'] = (df[f'VH_{season}'] - df[f'VV_{season}']) / (df[f'VH_{season}'] + df[f'VV_{season}'])\n"," df[f'SAR_DVI_{season}'] = df[f'VH_{season}'] - df[f'VV_{season}']\n"," df[f'SAR_SVI_{season}'] = df[f'VH_{season}'] + df[f'VV_{season}']\n"," df[f'SAR_RDVI_{season}'] = (df[f'VH_{season}'] / df[f'VV_{season}']) - (df[f'VV_{season}'] / df[f'VH_{season}'])\n"," df[f'SAR_NRDVI_{season}'] = ((df[f'VH_{season}']/df[f'VV_{season}'] - df[f'VV_{season}']/df[f'VH_{season}']) / (df[f'VH_{season}']/df[f'VV_{season}'] + df[f'VV_{season}']/df[f'VH_{season}']))\n"," df[f'SAR_SSDVI_{season}'] = df[f'VH_{season}']**2 - df[f'VV_{season}']**2\n","\n","def add_s2_indices(df):\n"," for season in seasons:\n"," # Optical Indices\n"," df[f'NDVI_{season}'] = (df[f'B8_{season}'] - df[f'B4_{season}']) / (df[f'B8_{season}'] + df[f'B4_{season}'])\n"," df[f'NDWI_{season}'] = (df[f'B8_{season}'] - df[f'B12_{season}']) / (df[f'B8_{season}'] + df[f'B12_{season}'])\n"," df[f'EVI_{season}'] = (2.5 * ((df[f'B8_{season}'] - df[f'B4_{season}']) / (df[f'B8_{season}'] + 6*df[f'B4_{season}'] - 7.5*df[f'B2_{season}'] + 1)))\n"," df[f'OSAVI_{season}'] = (df[f'B8_{season}'] - df[f'B4_{season}']) / (df[f'B8_{season}'] + df[f'B4_{season}'] + 0.16)\n"," df[f'ARVI_{season}'] = (df[f'B8_{season}'] - 2*df[f'B4_{season}'] + df[f'B2_{season}']) / (df[f'B8_{season}'] + 2*df[f'B4_{season}'] + df[f'B2_{season}'])\n"," df[f'VARI_{season}'] = (df[f'B3_{season}'] - df[f'B4_{season}']) / (df[f'B3_{season}'] + df[f'B4_{season}'] - df[f'B2_{season}'])\n"]},{"cell_type":"code","execution_count":26,"metadata":{"executionInfo":{"elapsed":7,"status":"ok","timestamp":1775402433241,"user":{"displayName":"CoRE Stack","userId":"15795073488475210764"},"user_tz":-330},"id":"iI0adElOftju"},"outputs":[],"source":["# def get_csv_data(fileName):\n","# data = pd.DataFrame()\n","# try:\n","# data = pd.read_csv(fileName)\n","# except Exception as exp:\n","# print(\"Error reading file \", fileName, \" - \", exp)\n","# return data\n","\n","def get_csv_chunks(fileName, chunksize=100_000):\n"," \"\"\"Yield DataFrame chunks from a CSV file.\"\"\"\n"," try:\n"," for chunk in pd.read_csv(fileName, chunksize=chunksize):\n"," yield chunk\n"," except Exception as exp:\n"," print(\"Error reading file \", fileName, \" - \", exp)\n"," return []\n"]},{"cell_type":"code","execution_count":27,"metadata":{"executionInfo":{"elapsed":2,"status":"ok","timestamp":1775402436472,"user":{"displayName":"CoRE Stack","userId":"15795073488475210764"},"user_tz":-330},"id":"BSIdWoUzfw7S"},"outputs":[],"source":["# For Canopy Height\n","def pipeline(fileName, chunksize=100000):\n"," res_chunks = [] # collect processed results\n","\n"," for df in get_csv_chunks(fileName, chunksize):\n"," if len(df) == 0:\n"," continue\n","\n"," # Add indices\n"," add_s1_indices(df)\n"," add_s2_indices(df)\n","\n"," # Geo column\n"," res_df = pd.DataFrame()\n"," res_df['.geo'] = df['.geo']\n","\n"," # Predictions\n"," pred_y_98 = model_rh98.predict(df[features_rh98])\n"," pred_y_75 = model_rh75.predict(df[features_rh75])\n"," pred_y_50 = model_rh50.predict(df[features_rh50])\n","\n"," res_df['rh98_class'] = pred_y_98\n"," res_df['rh75_class'] = pred_y_75\n"," res_df['rh50_class'] = pred_y_50\n","\n"," res_chunks.append(res_df)\n","\n"," if res_chunks:\n"," return pd.concat(res_chunks, ignore_index=True)\n"," else:\n"," return pd.DataFrame(columns=['.geo', 'rh98_class', 'rh75_class', 'rh50_class'])\n","\n","# def pipeline(fileName):\n","# # print(fileName)\n","\n","# df = get_csv_data(fileName)\n","\n","# if (len(df) == 0):\n","# return df\n","\n","# add_s1_indices(df)\n","# add_s2_indices(df)\n","\n","# geoList = list(df['.geo'])\n","# res_df = pd.DataFrame()\n","# res_df['.geo'] = geoList\n","\n","# test_df = df[features_rh98]\n","# pred_y_98 = list(model_rh98.predict(test_df))\n","# test_df = df[features_rh75]\n","# pred_y_75 = list(model_rh75.predict(test_df))\n","# test_df = df[features_rh50]\n","# pred_y_50 = list(model_rh50.predict(test_df))\n","\n","# res_df['rh98_class'] = pred_y_98\n","# res_df['rh75_class'] = pred_y_75\n","# res_df['rh50_class'] = pred_y_50\n","\n","# return res_df"]},{"cell_type":"code","execution_count":30,"metadata":{"colab":{"base_uri":"https://localhost:8080/"},"id":"R7GDTVjcf1Vw","outputId":"c0389988-dd39-4d7e-b22a-43b92fc67fae","executionInfo":{"status":"ok","timestamp":1775402686426,"user_tz":-330,"elapsed":53554,"user":{"displayName":"CoRE Stack","userId":"15795073488475210764"}}},"outputs":[{"output_type":"stream","name":"stdout","text":["Anantapur\n","['Southern Plateau and Hills Region']\n","\n","0 District: Anantapur, Zone: Southern Plateau and Hills Region, Year: 2017\n","no. of files: 1 \n","\n"]}],"source":["for year in years:\n"," dist_num = 0\n","\n"," for ind in district_mapping_df.index:\n"," # if dist_num < 14:\n"," # dist_num += 1\n"," # continue\n"," district = district_mapping_df.loc[ind, 'District']\n"," print(district)\n"," zones = district_mapping_df['IntersectingZones'][ind]\n"," print(zones)\n"," merged_df = pd.DataFrame()\n"," for zone in zones:\n"," print(f'\\n{dist_num} District: {district}, Zone: {zone}, Year: {year}')\n"," # dist_data_path = f'/content/drive/MyDrive/{agroclimaticZone_acronym_dict[zone]}_{district}_{year}/'\n"," dist_data_path = f'/content/drive/MyDrive/{agroclimaticZone_acronym_dict[zone]}_{year}'\n"," files = glob(f\"{dist_data_path}/{district}_{year}_all_grids.csv\")\n"," print(\"no. of files:\", len(files), '\\n')\n"," for fileName in files:\n"," df = pipeline(fileName,chunksize=100000)\n"," merged_df = pd.concat([merged_df, df], ignore_index=True)\n","\n"," merged_df.to_csv(f'/content/drive/MyDrive/TreeHealth/{agroclimatic_zone}/{district}/{year}/result_chm.csv', index=False)\n"," dist_num += 1"]},{"cell_type":"markdown","metadata":{"id":"vkgtoxewcscO"},"source":["# CCD"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"651ANiZ4ctgL"},"outputs":[],"source":["df = pd.read_csv(f'/content/drive/MyDrive/TreeHealth/Agroclimatic_regions/{agroclimatic_zone}.csv')\n","dist_list = list(df['Name'])\n","# dist_list = ['Yamunanagar'] ## Added extra"]},{"cell_type":"code","execution_count":null,"metadata":{"colab":{"base_uri":"https://localhost:8080/"},"executionInfo":{"elapsed":55,"status":"ok","timestamp":1768374034034,"user":{"displayName":"CoRE Stack","userId":"15795073488475210764"},"user_tz":-330},"id":"UvH9HrjK4w1E","outputId":"576ff54b-1b7a-4122-e215-f128724e1e1b"},"outputs":[{"name":"stdout","output_type":"stream","text":["86\n","['Araria', 'Arwal', 'AurangabadB', 'Banka', 'Begusarai', 'Bhagalpur', 'Bhojpur', 'Buxar', 'Darbhanga', 'Gaya', 'Gopalganj', 'Jamui', 'Jehanabad', 'Kaimur', 'Katihar', 'Khagaria', 'Kishanganj', 'Lakhisarai', 'Madhepura', 'Madhubani', 'Munger', 'Muzaffarpur', 'Nalanda', 'Nawada', 'Pashchim Champaran', 'Patna', 'Purba Champaran', 'Purnia', 'Rohtas', 'Saharsa', 'Samastipur', 'Saran', 'Sheikhpura', 'Sheohar', 'Sitamarhi', 'Siwan', 'Supaul', 'Vaishali', 'BalrampurC', 'Chatra', 'Deoghar', 'Dumka', 'Garhwa', 'Giridih', 'Godda', 'Hazaribagh', 'Kodarma', 'Pakur', 'Palamu', 'Sahibganj', 'Rewa', 'Singrauli', 'Allahabad', 'Ambedkar Nagar', 'Amethi', 'Azamgarh', 'Bahraich', 'Ballia', 'Balrampur', 'Barabanki', 'Basti', 'Chandauli', 'Deoria', 'Faizabad', 'Ghazipur', 'Gonda', 'Gorakhpur', 'Jaunpur', 'Kushinagar', 'Lakhimpur Kheri', 'Maharajganj', 'Mau', 'Mirzapur', 'Pratapgarhup', 'Sant Kabir Nagar', 'Sant Ravi Das Nagar', 'Shravasti', 'Siddharth Nagar', 'Sitapur', 'Sonbhadra', 'Sultanpur', 'Varanasi', 'Darjiling', 'Maldah', 'Murshidabad', 'Uttar Dinajpur']\n"]}],"source":["print(len(dist_list))\n","print(dist_list)"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"bMLuCQdz482a"},"outputs":[],"source":["agroclimatic_zone_model_path_mapping = {'Central Plateau & Hills Region': '/content/drive/MyDrive/TreeHealth/best_models/corrected/Central_Plateau_Hills_Region_toa_monthly_cover_51.joblib',\n"," 'Lower Gangetic Plain Region': '/content/drive/MyDrive/TreeHealth/best_models/corrected/Lower_Gangetic_Plain_Region_toa_monthly_cover_48.joblib',\n"," 'Middle Gangetic Plain Region': '/content/drive/MyDrive/TreeHealth/best_models/corrected/Middle_Gangetic_Plain_Region_toa_monthly_cover_50.joblib',\n"," 'Eastern Himalayan Region': '/content/drive/MyDrive/TreeHealth/best_models/corrected/Eastern_Himalayan_Region_toa_monthly_cover_86.joblib',\n"," 'Western Himalayan Region': '/content/drive/MyDrive/TreeHealth/best_models/corrected/Western_Himalayan_Region_toa_monthly_cover_78.joblib',\n"," 'Upper Gangetic Plain Region': '/content/drive/MyDrive/TreeHealth/best_models/corrected/Upper_Gangetic_Plain_Region_toa_monthly_cover_67.joblib',\n"," 'Trans Gangetic Plain Region': '/content/drive/MyDrive/TreeHealth/best_models/corrected/Trans_Gangetic_Plain_Region_toa_monthly_cover_55.joblib',\n"," 'East Coast Plains & Hills Region': '/content/drive/MyDrive/TreeHealth/best_models/corrected/East_Coast_Plains_Hills_Region_toa_monthly_cover_67.joblib',\n"," 'Eastern Plateau & Hills Region': '/content/drive/MyDrive/TreeHealth/best_models/corrected/Eastern_Plateau_Hills_Region_toa_monthly_cover_60.joblib',\n"," 'Western Plateau and Hills Region': '/content/drive/MyDrive/TreeHealth/best_models/corrected/Western_Plateau_and_Hills_Region_toa_monthly_cover_57.joblib',\n"," 'Southern Plateau and Hills Region': '/content/drive/MyDrive/TreeHealth/best_models/corrected/Southern_Plateau_and_Hills_Region_toa_monthly_cover_62.joblib'}\n"]},{"cell_type":"code","execution_count":null,"metadata":{"colab":{"base_uri":"https://localhost:8080/"},"executionInfo":{"elapsed":2483,"status":"ok","timestamp":1768375137014,"user":{"displayName":"CoRE Stack","userId":"15795073488475210764"},"user_tz":-330},"id":"df76932e5Hzw","outputId":"a802cb3b-506c-4404-ce7c-08fc46161e67"},"outputs":[{"name":"stderr","output_type":"stream","text":["/usr/lib/python3.12/pickle.py:1760: UserWarning: [07:18:56] WARNING: /workspace/src/collective/../data/../common/error_msg.h:83: If you are loading a serialized model (like pickle in Python, RDS in R) or\n","configuration generated by an older version of XGBoost, please export the model by calling\n","`Booster.save_model` from that version first, then load it back in current version. See:\n","\n"," https://xgboost.readthedocs.io/en/stable/tutorials/saving_model.html\n","\n","for more details about differences between saving model and serializing.\n","\n"," setstate(state)\n"]}],"source":["MODEL_PATH_cc = agroclimatic_zone_model_path_mapping[agroclimatic_zone]\n","model_cc = joblib.load(MODEL_PATH_cc)"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"tlVIdXqx5Lcr"},"outputs":[],"source":["if hasattr(model_cc, 'feature_names_in_'):\n"," features_cc = model_cc.feature_names_in_"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"r1sgggp16NBx"},"outputs":[],"source":["seasons = ['kharif', 'rabi', 'zaid']\n"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"_gp52vxf6P5X"},"outputs":[],"source":["def add_s1_indices(df):\n"," for season in seasons:\n"," # SAR Indices\n"," df[f'VV_VH_Ratio_{season}'] = df[f'VV_{season}'] / df[f'VH_{season}']\n"," df[f'VH_VV_Ratio_{season}'] = df[f'VH_{season}'] / df[f'VV_{season}']\n"," df[f'SAR_NDVI_{season}'] = (df[f'VH_{season}'] - df[f'VV_{season}']) / (df[f'VH_{season}'] + df[f'VV_{season}'])\n"," df[f'SAR_DVI_{season}'] = df[f'VH_{season}'] - df[f'VV_{season}']\n"," df[f'SAR_SVI_{season}'] = df[f'VH_{season}'] + df[f'VV_{season}']\n"," df[f'SAR_RDVI_{season}'] = (df[f'VH_{season}'] / df[f'VV_{season}']) - (df[f'VV_{season}'] / df[f'VH_{season}'])\n"," df[f'SAR_NRDVI_{season}'] = ((df[f'VH_{season}']/df[f'VV_{season}'] - df[f'VV_{season}']/df[f'VH_{season}']) / (df[f'VH_{season}']/df[f'VV_{season}'] + df[f'VV_{season}']/df[f'VH_{season}']))\n"," df[f'SAR_SSDVI_{season}'] = df[f'VH_{season}']**2 - df[f'VV_{season}']**2\n","\n","def add_s2_indices(df):\n"," for season in seasons:\n"," # Optical Indices\n"," df[f'NDVI_{season}'] = (df[f'B8_{season}'] - df[f'B4_{season}']) / (df[f'B8_{season}'] + df[f'B4_{season}'])\n"," df[f'NDWI_{season}'] = (df[f'B8_{season}'] - df[f'B12_{season}']) / (df[f'B8_{season}'] + df[f'B12_{season}'])\n"," df[f'EVI_{season}'] = (2.5 * ((df[f'B8_{season}'] - df[f'B4_{season}']) / (df[f'B8_{season}'] + 6*df[f'B4_{season}'] - 7.5*df[f'B2_{season}'] + 1)))\n"," df[f'OSAVI_{season}'] = (df[f'B8_{season}'] - df[f'B4_{season}']) / (df[f'B8_{season}'] + df[f'B4_{season}'] + 0.16)\n"," df[f'ARVI_{season}'] = (df[f'B8_{season}'] - 2*df[f'B4_{season}'] + df[f'B2_{season}']) / (df[f'B8_{season}'] + 2*df[f'B4_{season}'] + df[f'B2_{season}'])\n"," df[f'VARI_{season}'] = (df[f'B3_{season}'] - df[f'B4_{season}']) / (df[f'B3_{season}'] + df[f'B4_{season}'] - df[f'B2_{season}'])\n"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"Vg9Yf9nC6Sug"},"outputs":[],"source":["# def get_csv_data(fileName):\n","# data = pd.DataFrame()\n","# try:\n","# data = pd.read_csv(fileName)\n","# except Exception as exp:\n","# print(\"Error reading file \", fileName, \" - \", exp)\n","# return data\n","\n","def get_csv_chunks(fileName, chunksize=100_000):\n"," \"\"\"Yield DataFrame chunks from a CSV file.\"\"\"\n"," try:\n"," for chunk in pd.read_csv(fileName, chunksize=chunksize):\n"," yield chunk\n"," except Exception as exp:\n"," print(\"Error reading file \", fileName, \" - \", exp)\n"," return []"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"FYuZQqc36WKZ"},"outputs":[],"source":["# For Canopy Cover\n","def pipeline(fileName, chunksize=100000):\n"," res_chunks = [] # collect processed results\n","\n"," for df in get_csv_chunks(fileName, chunksize):\n"," if len(df) == 0:\n"," continue\n","\n"," # Add indices\n"," add_s1_indices(df)\n"," add_s2_indices(df)\n","\n"," # Geo column\n"," res_df = pd.DataFrame()\n"," res_df['.geo'] = list(df['.geo'])\n","\n"," for month in range(1,13):\n"," df['month_sin'] = [np.sin(2 * np.pi * month / 12)] * len(df)\n"," df['month_cos'] = [np.cos(2 * np.pi * month / 12)] * len(df)\n","\n"," test_df = df[features_cc]\n"," pred_y_cc = list(model_cc.predict(test_df))\n"," res_df[f'cc_{month}'] = pred_y_cc\n","\n"," res_chunks.append(res_df)\n","\n"," if res_chunks:\n"," return pd.concat(res_chunks, ignore_index=True)\n"," else:\n"," return pd.DataFrame(columns=['.geo'])\n","\n","# def pipeline(fileName):\n","\n","# print(fileName)\n","\n","# df = get_csv_data(fileName)\n","\n","# if (len(df) == 0):\n","# return df\n","\n","# add_s1_indices(df)\n","# add_s2_indices(df)\n","\n","# geoList = list(df['.geo'])\n","# res_df = pd.DataFrame()\n","# res_df['.geo'] = geoList\n","\n","# for month in range(1,13):\n","# df['month_sin'] = [np.sin(2 * np.pi * month / 12)] * len(df)\n","# df['month_cos'] = [np.cos(2 * np.pi * month / 12)] * len(df)\n","\n","# test_df = df[features_cc]\n","# pred_y_cc = list(model_cc.predict(test_df))\n","# res_df[f'cc_{month}'] = pred_y_cc\n","\n","# return res_df\n"]},{"cell_type":"code","execution_count":null,"metadata":{"colab":{"base_uri":"https://localhost:8080/"},"executionInfo":{"elapsed":22162462,"status":"ok","timestamp":1768405622097,"user":{"displayName":"CoRE Stack","userId":"15795073488475210764"},"user_tz":-330},"id":"9bXlL4RT6Yzo","outputId":"e89c99f5-0fb8-4a44-9c4b-651d13b8d8ea"},"outputs":[{"name":"stdout","output_type":"stream","text":["\n"," 0 Araria 2021\n","/content/drive/MyDrive/MGPR_2021\n","no. of files: 1 \n","\n","merged_df 1976372\n","\n"," 1 Arwal 2021\n","/content/drive/MyDrive/MGPR_2021\n","no. of files: 1 \n","\n","merged_df 192050\n","\n"," 2 AurangabadB 2021\n","/content/drive/MyDrive/MGPR_2021\n","no. of files: 1 \n","\n","merged_df 931520\n","\n"," 3 Banka 2021\n","/content/drive/MyDrive/MGPR_2021\n","no. of files: 1 \n","\n","merged_df 1794108\n","\n"," 4 Begusarai 2021\n","/content/drive/MyDrive/MGPR_2021\n","no. of files: 1 \n","\n","merged_df 925926\n","\n"," 5 Bhagalpur 2021\n","/content/drive/MyDrive/MGPR_2021\n","no. of files: 1 \n","\n","merged_df 1017500\n","\n"," 6 Bhojpur 2021\n","/content/drive/MyDrive/MGPR_2021\n","no. of files: 1 \n","\n","merged_df 528965\n","\n"," 7 Buxar 2021\n","/content/drive/MyDrive/MGPR_2021\n","no. of files: 1 \n","\n","merged_df 319698\n","\n"," 8 Darbhanga 2021\n","/content/drive/MyDrive/MGPR_2021\n","no. of files: 1 \n","\n","merged_df 1482059\n","\n"," 9 Gaya 2021\n","/content/drive/MyDrive/MGPR_2021\n","no. of files: 1 \n","\n","merged_df 2347878\n","\n"," 10 Gopalganj 2021\n","/content/drive/MyDrive/MGPR_2021\n","no. of files: 1 \n","\n","merged_df 960264\n","\n"," 11 Jamui 2021\n","/content/drive/MyDrive/MGPR_2021\n","no. of files: 1 \n","\n","merged_df 1946203\n","\n"," 12 Jehanabad 2021\n","/content/drive/MyDrive/MGPR_2021\n","no. of files: 1 \n","\n","merged_df 328318\n","\n"," 13 Kaimur 2021\n","/content/drive/MyDrive/MGPR_2021\n","no. of files: 1 \n","\n","merged_df 2050428\n","\n"," 14 Katihar 2021\n","/content/drive/MyDrive/MGPR_2021\n","no. of files: 1 \n","\n","merged_df 1742730\n","\n"," 15 Khagaria 2021\n","/content/drive/MyDrive/MGPR_2021\n","no. of files: 1 \n","\n","merged_df 763589\n","\n"," 16 Kishanganj 2021\n","/content/drive/MyDrive/MGPR_2021\n","no. of files: 1 \n","\n","merged_df 1080963\n","\n"," 17 Lakhisarai 2021\n","/content/drive/MyDrive/MGPR_2021\n","no. of files: 1 \n","\n","merged_df 574227\n","\n"," 18 Madhepura 2021\n","/content/drive/MyDrive/MGPR_2021\n","no. of files: 1 \n","\n","merged_df 1485046\n","\n"," 19 Madhubani 2021\n","/content/drive/MyDrive/MGPR_2021\n","no. of files: 1 \n","\n","merged_df 2275458\n","\n"," 20 Munger 2021\n","/content/drive/MyDrive/MGPR_2021\n","no. of files: 1 \n","\n","merged_df 788345\n","\n"," 21 Muzaffarpur 2021\n","/content/drive/MyDrive/MGPR_2021\n","no. of files: 1 \n","\n","merged_df 2058580\n","\n"," 22 Nalanda 2021\n","/content/drive/MyDrive/MGPR_2021\n","no. of files: 1 \n","\n","merged_df 828451\n","\n"," 23 Nawada 2021\n","/content/drive/MyDrive/MGPR_2021\n","no. of files: 1 \n","\n","merged_df 1456076\n","\n"," 24 Pashchim Champaran 2021\n","/content/drive/MyDrive/MGPR_2021\n","no. of files: 1 \n","\n","merged_df 5057819\n","\n"," 25 Patna 2021\n","/content/drive/MyDrive/MGPR_2021\n","no. of files: 1 \n","\n","merged_df 988946\n","\n"," 26 Purba Champaran 2021\n","/content/drive/MyDrive/MGPR_2021\n","no. of files: 1 \n","\n","merged_df 2114183\n","\n"," 27 Purnia 2021\n","/content/drive/MyDrive/MGPR_2021\n","no. of files: 1 \n","\n","merged_df 2585720\n","\n"," 28 Rohtas 2021\n","/content/drive/MyDrive/MGPR_2021\n","no. of files: 1 \n","\n","merged_df 1802430\n","\n"," 29 Saharsa 2021\n","/content/drive/MyDrive/MGPR_2021\n","no. of files: 1 \n","\n","merged_df 1403136\n","\n"," 30 Samastipur 2021\n","/content/drive/MyDrive/MGPR_2021\n","no. of files: 1 \n","\n","merged_df 1863560\n","\n"," 31 Saran 2021\n","/content/drive/MyDrive/MGPR_2021\n","no. of files: 1 \n","\n","merged_df 1191174\n","\n"," 32 Sheikhpura 2021\n","/content/drive/MyDrive/MGPR_2021\n","no. of files: 1 \n","\n","merged_df 164235\n","\n"," 33 Sheohar 2021\n","/content/drive/MyDrive/MGPR_2021\n","no. of files: 1 \n","\n","merged_df 382768\n","\n"," 34 Sitamarhi 2021\n","/content/drive/MyDrive/MGPR_2021\n","no. of files: 1 \n","\n","merged_df 1455242\n","\n"," 35 Siwan 2021\n","/content/drive/MyDrive/MGPR_2021\n","no. of files: 1 \n","\n","merged_df 942499\n","\n"," 36 Supaul 2021\n","/content/drive/MyDrive/MGPR_2021\n","no. of files: 1 \n","\n","merged_df 1771433\n","\n"," 37 Vaishali 2021\n","/content/drive/MyDrive/MGPR_2021\n","no. of files: 1 \n","\n","merged_df 1229291\n","\n"," 38 BalrampurC 2021\n","/content/drive/MyDrive/MGPR_2021\n","no. of files: 1 \n","\n","merged_df 28983\n","\n"," 39 Chatra 2021\n","/content/drive/MyDrive/MGPR_2021\n","no. of files: 1 \n","\n","merged_df 25378\n","\n"," 40 Deoghar 2021\n","/content/drive/MyDrive/MGPR_2021\n","no. of files: 1 \n","\n","merged_df 11084\n","\n"," 41 Dumka 2021\n","/content/drive/MyDrive/MGPR_2021\n","no. of files: 1 \n","\n","merged_df 7096\n","\n"," 42 Garhwa 2021\n","/content/drive/MyDrive/MGPR_2021\n","no. of files: 1 \n","\n","merged_df 36756\n","\n"," 43 Giridih 2021\n","/content/drive/MyDrive/MGPR_2021\n","no. of files: 1 \n","\n","merged_df 7006\n","\n"," 44 Godda 2021\n","/content/drive/MyDrive/MGPR_2021\n","no. of files: 1 \n","\n","merged_df 1900116\n","\n"," 45 Hazaribagh 2021\n","/content/drive/MyDrive/MGPR_2021\n","no. of files: 1 \n","\n","merged_df 19337\n","\n"," 46 Kodarma 2021\n","/content/drive/MyDrive/MGPR_2021\n","no. of files: 1 \n","\n","merged_df 41050\n","\n"," 47 Pakur 2021\n","/content/drive/MyDrive/MGPR_2021\n","no. of files: 1 \n","\n","merged_df 30727\n","\n"," 48 Palamu 2021\n","/content/drive/MyDrive/MGPR_2021\n","no. of files: 1 \n","\n","merged_df 11864\n","\n"," 49 Sahibganj 2021\n","/content/drive/MyDrive/MGPR_2021\n","no. of files: 1 \n","\n","merged_df 2025865\n","\n"," 50 Rewa 2021\n","/content/drive/MyDrive/MGPR_2021\n","no. of files: 1 \n","\n","merged_df 18058\n","\n"," 51 Singrauli 2021\n","/content/drive/MyDrive/MGPR_2021\n","no. of files: 1 \n","\n","merged_df 12480\n","\n"," 52 Allahabad 2021\n","/content/drive/MyDrive/MGPR_2021\n","no. of files: 1 \n","\n","merged_df 4270\n","\n"," 53 Ambedkar Nagar 2021\n","/content/drive/MyDrive/MGPR_2021\n","no. of files: 1 \n","\n","merged_df 923541\n","\n"," 54 Amethi 2021\n","/content/drive/MyDrive/MGPR_2021\n","no. of files: 1 \n","\n","merged_df 13299\n","\n"," 55 Azamgarh 2021\n","/content/drive/MyDrive/MGPR_2021\n","no. of files: 1 \n","\n","merged_df 1182006\n","\n"," 56 Bahraich 2021\n","/content/drive/MyDrive/MGPR_2021\n","no. of files: 1 \n","\n","merged_df 2344420\n","\n"," 57 Ballia 2021\n","/content/drive/MyDrive/MGPR_2021\n","no. of files: 1 \n","\n","merged_df 670018\n","\n"," 58 Balrampur 2021\n","/content/drive/MyDrive/MGPR_2021\n","no. of files: 1 \n","\n","merged_df 2339692\n","\n"," 59 Barabanki 2021\n","/content/drive/MyDrive/MGPR_2021\n","no. of files: 1 \n","\n","merged_df 423163\n","\n"," 60 Basti 2021\n","/content/drive/MyDrive/MGPR_2021\n","no. of files: 1 \n","\n","merged_df 1061160\n","\n"," 61 Chandauli 2021\n","/content/drive/MyDrive/MGPR_2021\n","no. of files: 1 \n","\n","merged_df 1213119\n","\n"," 62 Deoria 2021\n","/content/drive/MyDrive/MGPR_2021\n","no. of files: 1 \n","\n","merged_df 682125\n","\n"," 63 Faizabad 2021\n","/content/drive/MyDrive/MGPR_2021\n","no. of files: 1 \n","\n","merged_df 1241511\n","\n"," 64 Ghazipur 2021\n","/content/drive/MyDrive/MGPR_2021\n","no. of files: 1 \n","\n","merged_df 796377\n","\n"," 65 Gonda 2021\n","/content/drive/MyDrive/MGPR_2021\n","no. of files: 1 \n","\n","merged_df 2413423\n","\n"," 66 Gorakhpur 2021\n","/content/drive/MyDrive/MGPR_2021\n","no. of files: 1 \n","\n","merged_df 772243\n","\n"," 67 Jaunpur 2021\n","/content/drive/MyDrive/MGPR_2021\n","no. of files: 1 \n","\n","merged_df 1447752\n","\n"," 68 Kushinagar 2021\n","/content/drive/MyDrive/MGPR_2021\n","no. of files: 1 \n","\n","merged_df 2092368\n","\n"," 69 Lakhimpur Kheri 2021\n","/content/drive/MyDrive/MGPR_2021\n","no. of files: 1 \n","\n","merged_df 411\n","\n"," 70 Maharajganj 2021\n","/content/drive/MyDrive/MGPR_2021\n","no. of files: 1 \n","\n","merged_df 1562109\n","\n"," 71 Mau 2021\n","/content/drive/MyDrive/MGPR_2021\n","no. of files: 1 \n","\n","merged_df 601321\n","\n"," 72 Mirzapur 2021\n","/content/drive/MyDrive/MGPR_2021\n","no. of files: 1 \n","\n","merged_df 1617620\n","\n"," 73 Pratapgarhup 2021\n","/content/drive/MyDrive/MGPR_2021\n","no. of files: 1 \n","\n","merged_df 596\n","\n"," 74 Sant Kabir Nagar 2021\n","/content/drive/MyDrive/MGPR_2021\n","no. of files: 1 \n","\n","merged_df 365259\n","\n"," 75 Sant Ravi Das Nagar 2021\n","/content/drive/MyDrive/MGPR_2021\n","no. of files: 1 \n","\n","merged_df 312896\n","\n"," 76 Shravasti 2021\n","/content/drive/MyDrive/MGPR_2021\n","no. of files: 1 \n","\n","merged_df 1762200\n","\n"," 77 Siddharth Nagar 2021\n","/content/drive/MyDrive/MGPR_2021\n","no. of files: 1 \n","\n","merged_df 843547\n","\n"," 78 Sitapur 2021\n","/content/drive/MyDrive/MGPR_2021\n","no. of files: 1 \n","\n","merged_df 42\n","\n"," 79 Sonbhadra 2021\n","/content/drive/MyDrive/MGPR_2021\n","no. of files: 1 \n","\n","merged_df 5833979\n","\n"," 80 Sultanpur 2021\n","/content/drive/MyDrive/MGPR_2021\n","no. of files: 1 \n","\n","merged_df 16803\n","\n"," 81 Varanasi 2021\n","/content/drive/MyDrive/MGPR_2021\n","no. of files: 1 \n","\n","merged_df 552786\n","\n"," 82 Darjiling 2021\n","/content/drive/MyDrive/MGPR_2021\n","no. of files: 1 \n","\n","merged_df 16059\n","\n"," 83 Maldah 2021\n","/content/drive/MyDrive/MGPR_2021\n","no. of files: 1 \n","\n","merged_df 19259\n","\n"," 84 Murshidabad 2021\n","/content/drive/MyDrive/MGPR_2021\n","no. of files: 1 \n","\n","merged_df 4328\n","\n"," 85 Uttar Dinajpur 2021\n","/content/drive/MyDrive/MGPR_2021\n","no. of files: 1 \n","\n","merged_df 47370\n","\n"," 0 Araria 2022\n","/content/drive/MyDrive/MGPR_2022\n","no. of files: 1 \n","\n","merged_df 2190491\n","\n"," 1 Arwal 2022\n","/content/drive/MyDrive/MGPR_2022\n","no. of files: 1 \n","\n","merged_df 161470\n","\n"," 2 AurangabadB 2022\n","/content/drive/MyDrive/MGPR_2022\n","no. of files: 1 \n","\n","merged_df 835319\n","\n"," 3 Banka 2022\n","/content/drive/MyDrive/MGPR_2022\n","no. of files: 1 \n","\n","merged_df 1584408\n","\n"," 4 Begusarai 2022\n","/content/drive/MyDrive/MGPR_2022\n","no. of files: 1 \n","\n","merged_df 824518\n","\n"," 5 Bhagalpur 2022\n","/content/drive/MyDrive/MGPR_2022\n","no. of files: 1 \n","\n","merged_df 905853\n","\n"," 6 Bhojpur 2022\n","/content/drive/MyDrive/MGPR_2022\n","no. of files: 1 \n","\n","merged_df 488248\n","\n"," 7 Buxar 2022\n","/content/drive/MyDrive/MGPR_2022\n","no. of files: 1 \n","\n","merged_df 305113\n","\n"," 8 Darbhanga 2022\n","/content/drive/MyDrive/MGPR_2022\n","no. of files: 1 \n","\n","merged_df 1331421\n","\n"," 9 Gaya 2022\n","/content/drive/MyDrive/MGPR_2022\n","no. of files: 1 \n","\n","merged_df 2161066\n","\n"," 10 Gopalganj 2022\n","/content/drive/MyDrive/MGPR_2022\n","no. of files: 1 \n","\n","merged_df 861413\n","\n"," 11 Jamui 2022\n","/content/drive/MyDrive/MGPR_2022\n","no. of files: 1 \n","\n","merged_df 1876656\n","\n"," 12 Jehanabad 2022\n","/content/drive/MyDrive/MGPR_2022\n","no. of files: 1 \n","\n","merged_df 299666\n","\n"," 13 Kaimur 2022\n","/content/drive/MyDrive/MGPR_2022\n","no. of files: 1 \n","\n","merged_df 1941287\n","\n"," 14 Katihar 2022\n","/content/drive/MyDrive/MGPR_2022\n","no. of files: 1 \n","\n","merged_df 1475151\n","\n"," 15 Khagaria 2022\n","/content/drive/MyDrive/MGPR_2022\n","no. of files: 1 \n","\n","merged_df 588876\n","\n"," 16 Kishanganj 2022\n","/content/drive/MyDrive/MGPR_2022\n","no. of files: 1 \n","\n","merged_df 1179706\n","\n"," 17 Lakhisarai 2022\n","/content/drive/MyDrive/MGPR_2022\n","no. of files: 1 \n","\n","merged_df 539734\n","\n"," 18 Madhepura 2022\n","/content/drive/MyDrive/MGPR_2022\n","no. of files: 1 \n","\n","merged_df 1432748\n","\n"," 19 Madhubani 2022\n","/content/drive/MyDrive/MGPR_2022\n","no. of files: 1 \n","\n","merged_df 2590746\n","\n"," 20 Munger 2022\n","/content/drive/MyDrive/MGPR_2022\n","no. of files: 1 \n","\n","merged_df 757651\n","\n"," 21 Muzaffarpur 2022\n","/content/drive/MyDrive/MGPR_2022\n","no. of files: 1 \n","\n","merged_df 1891473\n","\n"," 22 Nalanda 2022\n","/content/drive/MyDrive/MGPR_2022\n","no. of files: 1 \n","\n","merged_df 758015\n","\n"," 23 Nawada 2022\n","/content/drive/MyDrive/MGPR_2022\n","no. of files: 1 \n","\n","merged_df 1309715\n","\n"," 24 Pashchim Champaran 2022\n","/content/drive/MyDrive/MGPR_2022\n","no. of files: 1 \n","\n","merged_df 4314974\n","\n"," 25 Patna 2022\n","/content/drive/MyDrive/MGPR_2022\n","no. of files: 1 \n","\n","merged_df 908794\n","\n"," 26 Purba Champaran 2022\n","/content/drive/MyDrive/MGPR_2022\n","no. of files: 1 \n","\n","merged_df 1949197\n","\n"," 27 Purnia 2022\n","/content/drive/MyDrive/MGPR_2022\n","no. of files: 1 \n","\n","merged_df 2201352\n","\n"," 28 Rohtas 2022\n","/content/drive/MyDrive/MGPR_2022\n","no. of files: 1 \n","\n","merged_df 1735872\n","\n"," 29 Saharsa 2022\n","/content/drive/MyDrive/MGPR_2022\n","no. of files: 1 \n","\n","merged_df 1163729\n","\n"," 30 Samastipur 2022\n","/content/drive/MyDrive/MGPR_2022\n","no. of files: 1 \n","\n","merged_df 1492263\n","\n"," 31 Saran 2022\n","/content/drive/MyDrive/MGPR_2022\n","no. of files: 1 \n","\n","merged_df 952250\n","\n"," 32 Sheikhpura 2022\n","/content/drive/MyDrive/MGPR_2022\n","no. of files: 1 \n","\n","merged_df 127904\n","\n"," 33 Sheohar 2022\n","/content/drive/MyDrive/MGPR_2022\n","no. of files: 1 \n","\n","merged_df 373246\n","\n"," 34 Sitamarhi 2022\n","/content/drive/MyDrive/MGPR_2022\n","no. of files: 1 \n","\n","merged_df 1418026\n","\n"," 35 Siwan 2022\n","/content/drive/MyDrive/MGPR_2022\n","no. of files: 1 \n","\n","merged_df 735087\n","\n"," 36 Supaul 2022\n","/content/drive/MyDrive/MGPR_2022\n","no. of files: 1 \n","\n","merged_df 2279975\n","\n"," 37 Vaishali 2022\n","/content/drive/MyDrive/MGPR_2022\n","no. of files: 1 \n","\n","merged_df 1067125\n","\n"," 38 BalrampurC 2022\n","/content/drive/MyDrive/MGPR_2022\n","no. of files: 1 \n","\n","merged_df 29894\n","\n"," 39 Chatra 2022\n","/content/drive/MyDrive/MGPR_2022\n","no. of files: 1 \n","\n","merged_df 23811\n","\n"," 40 Deoghar 2022\n","/content/drive/MyDrive/MGPR_2022\n","no. of files: 1 \n","\n","merged_df 10025\n","\n"," 41 Dumka 2022\n","/content/drive/MyDrive/MGPR_2022\n","no. of files: 1 \n","\n","merged_df 5602\n","\n"," 42 Garhwa 2022\n","/content/drive/MyDrive/MGPR_2022\n","no. of files: 1 \n","\n","merged_df 35540\n","\n"," 43 Giridih 2022\n","/content/drive/MyDrive/MGPR_2022\n","no. of files: 1 \n","\n","merged_df 7097\n","\n"," 44 Godda 2022\n","/content/drive/MyDrive/MGPR_2022\n","no. of files: 1 \n","\n","merged_df 1689963\n","\n"," 45 Hazaribagh 2022\n","/content/drive/MyDrive/MGPR_2022\n","no. of files: 1 \n","\n","merged_df 18602\n","\n"," 46 Kodarma 2022\n","/content/drive/MyDrive/MGPR_2022\n","no. of files: 1 \n","\n","merged_df 40475\n","\n"," 47 Pakur 2022\n","/content/drive/MyDrive/MGPR_2022\n","no. of files: 1 \n","\n","merged_df 30062\n","\n"," 48 Palamu 2022\n","/content/drive/MyDrive/MGPR_2022\n","no. of files: 1 \n","\n","merged_df 10438\n","\n"," 49 Sahibganj 2022\n","/content/drive/MyDrive/MGPR_2022\n","no. of files: 1 \n","\n","merged_df 1858377\n","\n"," 50 Rewa 2022\n","/content/drive/MyDrive/MGPR_2022\n","no. of files: 1 \n","\n","merged_df 18511\n","\n"," 51 Singrauli 2022\n","/content/drive/MyDrive/MGPR_2022\n","no. of files: 1 \n","\n","merged_df 12442\n","\n"," 52 Allahabad 2022\n","/content/drive/MyDrive/MGPR_2022\n","no. of files: 1 \n","\n","merged_df 4125\n","\n"," 53 Ambedkar Nagar 2022\n","/content/drive/MyDrive/MGPR_2022\n","no. of files: 1 \n","\n","merged_df 878644\n","\n"," 54 Amethi 2022\n","/content/drive/MyDrive/MGPR_2022\n","no. of files: 1 \n","\n","merged_df 12882\n","\n"," 55 Azamgarh 2022\n","/content/drive/MyDrive/MGPR_2022\n","no. of files: 1 \n","\n","merged_df 1144098\n","\n"," 56 Bahraich 2022\n","/content/drive/MyDrive/MGPR_2022\n","no. of files: 1 \n","\n","merged_df 2281433\n","\n"," 57 Ballia 2022\n","/content/drive/MyDrive/MGPR_2022\n","no. of files: 1 \n","\n","merged_df 626531\n","\n"," 58 Balrampur 2022\n","/content/drive/MyDrive/MGPR_2022\n","no. of files: 1 \n","\n","merged_df 2413530\n","\n"," 59 Barabanki 2022\n","/content/drive/MyDrive/MGPR_2022\n","no. of files: 1 \n","\n","merged_df 397978\n","\n"," 60 Basti 2022\n","/content/drive/MyDrive/MGPR_2022\n","no. of files: 1 \n","\n","merged_df 1032498\n","\n"," 61 Chandauli 2022\n","/content/drive/MyDrive/MGPR_2022\n","no. of files: 1 \n","\n","merged_df 1129638\n","\n"," 62 Deoria 2022\n","/content/drive/MyDrive/MGPR_2022\n","no. of files: 1 \n","\n","merged_df 669973\n","\n"," 63 Faizabad 2022\n","/content/drive/MyDrive/MGPR_2022\n","no. of files: 1 \n","\n","merged_df 1181990\n","\n"," 64 Ghazipur 2022\n","/content/drive/MyDrive/MGPR_2022\n","no. of files: 1 \n","\n","merged_df 731143\n","\n"," 65 Gonda 2022\n","/content/drive/MyDrive/MGPR_2022\n","no. of files: 1 \n","\n","merged_df 2484101\n","\n"," 66 Gorakhpur 2022\n","/content/drive/MyDrive/MGPR_2022\n","no. of files: 1 \n","\n","merged_df 745270\n","\n"," 67 Jaunpur 2022\n","/content/drive/MyDrive/MGPR_2022\n","no. of files: 1 \n","\n","merged_df 1444477\n","\n"," 68 Kushinagar 2022\n","/content/drive/MyDrive/MGPR_2022\n","no. of files: 1 \n","\n","merged_df 1983578\n","\n"," 69 Lakhimpur Kheri 2022\n","/content/drive/MyDrive/MGPR_2022\n","no. of files: 1 \n","\n","merged_df 405\n","\n"," 70 Maharajganj 2022\n","/content/drive/MyDrive/MGPR_2022\n","no. of files: 1 \n","\n","merged_df 1535072\n","\n"," 71 Mau 2022\n","/content/drive/MyDrive/MGPR_2022\n","no. of files: 1 \n","\n","merged_df 547318\n","\n"," 72 Mirzapur 2022\n","/content/drive/MyDrive/MGPR_2022\n","no. of files: 1 \n","\n","merged_df 1555020\n","\n"," 73 Pratapgarhup 2022\n","/content/drive/MyDrive/MGPR_2022\n","no. of files: 1 \n","\n","merged_df 569\n","\n"," 74 Sant Kabir Nagar 2022\n","/content/drive/MyDrive/MGPR_2022\n","no. of files: 1 \n","\n","merged_df 347484\n","\n"," 75 Sant Ravi Das Nagar 2022\n","/content/drive/MyDrive/MGPR_2022\n","no. of files: 1 \n","\n","merged_df 308932\n","\n"," 76 Shravasti 2022\n","/content/drive/MyDrive/MGPR_2022\n","no. of files: 1 \n","\n","merged_df 1696175\n","\n"," 77 Siddharth Nagar 2022\n","/content/drive/MyDrive/MGPR_2022\n","no. of files: 1 \n","\n","merged_df 815397\n","\n"," 78 Sitapur 2022\n","/content/drive/MyDrive/MGPR_2022\n","no. of files: 1 \n","\n","merged_df 49\n","\n"," 79 Sonbhadra 2022\n","/content/drive/MyDrive/MGPR_2022\n","no. of files: 1 \n","\n","merged_df 5575006\n","\n"," 80 Sultanpur 2022\n","/content/drive/MyDrive/MGPR_2022\n","no. of files: 1 \n","\n","merged_df 17301\n","\n"," 81 Varanasi 2022\n","/content/drive/MyDrive/MGPR_2022\n","no. of files: 1 \n","\n","merged_df 530860\n","\n"," 82 Darjiling 2022\n","/content/drive/MyDrive/MGPR_2022\n","no. of files: 1 \n","\n","merged_df 15889\n","\n"," 83 Maldah 2022\n","/content/drive/MyDrive/MGPR_2022\n","no. of files: 1 \n","\n","merged_df 15725\n","\n"," 84 Murshidabad 2022\n","/content/drive/MyDrive/MGPR_2022\n","no. of files: 1 \n","\n","merged_df 3520\n","\n"," 85 Uttar Dinajpur 2022\n","/content/drive/MyDrive/MGPR_2022\n","no. of files: 1 \n","\n","merged_df 49835\n","\n"," 0 Araria 2023\n","/content/drive/MyDrive/MGPR_2023\n","no. of files: 1 \n","\n","merged_df 1969950\n","\n"," 1 Arwal 2023\n","/content/drive/MyDrive/MGPR_2023\n","no. of files: 1 \n","\n","merged_df 172959\n","\n"," 2 AurangabadB 2023\n","/content/drive/MyDrive/MGPR_2023\n","no. of files: 1 \n","\n","merged_df 919333\n","\n"," 3 Banka 2023\n","/content/drive/MyDrive/MGPR_2023\n","no. of files: 1 \n","\n","merged_df 1719181\n","\n"," 4 Begusarai 2023\n","/content/drive/MyDrive/MGPR_2023\n","no. of files: 1 \n","\n","merged_df 826079\n","\n"," 5 Bhagalpur 2023\n","/content/drive/MyDrive/MGPR_2023\n","no. of files: 1 \n","\n","merged_df 947866\n","\n"," 6 Bhojpur 2023\n","/content/drive/MyDrive/MGPR_2023\n","no. of files: 1 \n","\n","merged_df 493242\n","\n"," 7 Buxar 2023\n","/content/drive/MyDrive/MGPR_2023\n","no. of files: 1 \n","\n","merged_df 315131\n","\n"," 8 Darbhanga 2023\n","/content/drive/MyDrive/MGPR_2023\n","no. of files: 1 \n","\n","merged_df 1446552\n","\n"," 9 Gaya 2023\n","/content/drive/MyDrive/MGPR_2023\n","no. of files: 1 \n","\n","merged_df 2250105\n","\n"," 10 Gopalganj 2023\n","/content/drive/MyDrive/MGPR_2023\n","no. of files: 1 \n","\n","merged_df 884977\n","\n"," 11 Jamui 2023\n","/content/drive/MyDrive/MGPR_2023\n","no. of files: 1 \n","\n","merged_df 2049016\n","\n"," 12 Jehanabad 2023\n","/content/drive/MyDrive/MGPR_2023\n","no. of files: 1 \n","\n","merged_df 295487\n","\n"," 13 Kaimur 2023\n","/content/drive/MyDrive/MGPR_2023\n","no. of files: 1 \n","\n","merged_df 1891094\n","\n"," 14 Katihar 2023\n","/content/drive/MyDrive/MGPR_2023\n","no. of files: 1 \n","\n","merged_df 1527107\n","\n"," 15 Khagaria 2023\n","/content/drive/MyDrive/MGPR_2023\n","no. of files: 1 \n","\n","merged_df 552961\n","\n"," 16 Kishanganj 2023\n","/content/drive/MyDrive/MGPR_2023\n","no. of files: 1 \n","\n","merged_df 1069558\n","\n"," 17 Lakhisarai 2023\n","/content/drive/MyDrive/MGPR_2023\n","no. of files: 1 \n","\n","merged_df 574275\n","\n"," 18 Madhepura 2023\n","/content/drive/MyDrive/MGPR_2023\n","no. of files: 1 \n","\n","merged_df 1326965\n","\n"," 19 Madhubani 2023\n","/content/drive/MyDrive/MGPR_2023\n","no. of files: 1 \n","\n","merged_df 2572389\n","\n"," 20 Munger 2023\n","/content/drive/MyDrive/MGPR_2023\n","no. of files: 1 \n","\n","merged_df 793397\n","\n"," 21 Muzaffarpur 2023\n","/content/drive/MyDrive/MGPR_2023\n","no. of files: 1 \n","\n","merged_df 1974039\n","\n"," 22 Nalanda 2023\n","/content/drive/MyDrive/MGPR_2023\n","no. of files: 1 \n","\n","merged_df 760888\n","\n"," 23 Nawada 2023\n","/content/drive/MyDrive/MGPR_2023\n","no. of files: 1 \n","\n","merged_df 1415526\n","\n"," 24 Pashchim Champaran 2023\n","/content/drive/MyDrive/MGPR_2023\n","no. of files: 1 \n","\n","merged_df 4607789\n","\n"," 25 Patna 2023\n","/content/drive/MyDrive/MGPR_2023\n","no. of files: 1 \n","\n","merged_df 878887\n","\n"," 26 Purba Champaran 2023\n","/content/drive/MyDrive/MGPR_2023\n","no. of files: 1 \n","\n","merged_df 1975397\n","\n"," 27 Purnia 2023\n","/content/drive/MyDrive/MGPR_2023\n","no. of files: 1 \n","\n","merged_df 2036293\n","\n"," 28 Rohtas 2023\n","/content/drive/MyDrive/MGPR_2023\n","no. of files: 1 \n","\n","merged_df 1740932\n","\n"," 29 Saharsa 2023\n","/content/drive/MyDrive/MGPR_2023\n","no. of files: 1 \n","\n","merged_df 1172223\n","\n"," 30 Samastipur 2023\n","/content/drive/MyDrive/MGPR_2023\n","no. of files: 1 \n","\n","merged_df 1579828\n","\n"," 31 Saran 2023\n","/content/drive/MyDrive/MGPR_2023\n","no. of files: 1 \n","\n","merged_df 967225\n","\n"," 32 Sheikhpura 2023\n","/content/drive/MyDrive/MGPR_2023\n","no. of files: 1 \n","\n","merged_df 139694\n","\n"," 33 Sheohar 2023\n","/content/drive/MyDrive/MGPR_2023\n","no. of files: 1 \n","\n","merged_df 350852\n","\n"," 34 Sitamarhi 2023\n","/content/drive/MyDrive/MGPR_2023\n","no. of files: 1 \n","\n","merged_df 1253511\n","\n"," 35 Siwan 2023\n","/content/drive/MyDrive/MGPR_2023\n","no. of files: 1 \n","\n","merged_df 748663\n","\n"," 36 Supaul 2023\n","/content/drive/MyDrive/MGPR_2023\n","no. of files: 1 \n","\n","merged_df 1911496\n","\n"," 37 Vaishali 2023\n","/content/drive/MyDrive/MGPR_2023\n","no. of files: 1 \n","\n","merged_df 1108101\n","\n"," 38 BalrampurC 2023\n","/content/drive/MyDrive/MGPR_2023\n","no. of files: 1 \n","\n","merged_df 29650\n","\n"," 39 Chatra 2023\n","/content/drive/MyDrive/MGPR_2023\n","no. of files: 1 \n","\n","merged_df 25041\n","\n"," 40 Deoghar 2023\n","/content/drive/MyDrive/MGPR_2023\n","no. of files: 1 \n","\n","merged_df 10766\n","\n"," 41 Dumka 2023\n","/content/drive/MyDrive/MGPR_2023\n","no. of files: 1 \n","\n","merged_df 6322\n","\n"," 42 Garhwa 2023\n","/content/drive/MyDrive/MGPR_2023\n","no. of files: 1 \n","\n","merged_df 38526\n","\n"," 43 Giridih 2023\n","/content/drive/MyDrive/MGPR_2023\n","no. of files: 1 \n","\n","merged_df 7574\n","\n"," 44 Godda 2023\n","/content/drive/MyDrive/MGPR_2023\n","no. of files: 1 \n","\n","merged_df 1777243\n","\n"," 45 Hazaribagh 2023\n","/content/drive/MyDrive/MGPR_2023\n","no. of files: 1 \n","\n","merged_df 18805\n","\n"," 46 Kodarma 2023\n","/content/drive/MyDrive/MGPR_2023\n","no. of files: 1 \n","\n","merged_df 40709\n","\n"," 47 Pakur 2023\n","/content/drive/MyDrive/MGPR_2023\n","no. of files: 1 \n","\n","merged_df 29874\n","\n"," 48 Palamu 2023\n","/content/drive/MyDrive/MGPR_2023\n","no. of files: 1 \n","\n","merged_df 12084\n","\n"," 49 Sahibganj 2023\n","/content/drive/MyDrive/MGPR_2023\n","no. of files: 1 \n","\n","merged_df 1923750\n","\n"," 50 Rewa 2023\n","/content/drive/MyDrive/MGPR_2023\n","no. of files: 1 \n","\n","merged_df 18630\n","\n"," 51 Singrauli 2023\n","/content/drive/MyDrive/MGPR_2023\n","no. of files: 1 \n","\n","merged_df 10951\n","\n"," 52 Allahabad 2023\n","/content/drive/MyDrive/MGPR_2023\n","no. of files: 1 \n","\n","merged_df 3656\n","\n"," 53 Ambedkar Nagar 2023\n","/content/drive/MyDrive/MGPR_2023\n","no. of files: 1 \n","\n","merged_df 820179\n","\n"," 54 Amethi 2023\n","/content/drive/MyDrive/MGPR_2023\n","no. of files: 1 \n","\n","merged_df 12372\n","\n"," 55 Azamgarh 2023\n","/content/drive/MyDrive/MGPR_2023\n","no. of files: 1 \n","\n","merged_df 1082775\n","\n"," 56 Bahraich 2023\n","/content/drive/MyDrive/MGPR_2023\n","no. of files: 1 \n","\n","merged_df 2313869\n","\n"," 57 Ballia 2023\n","/content/drive/MyDrive/MGPR_2023\n","no. of files: 1 \n","\n","merged_df 644920\n","\n"," 58 Balrampur 2023\n","/content/drive/MyDrive/MGPR_2023\n","no. of files: 1 \n","\n","merged_df 2341159\n","\n"," 59 Barabanki 2023\n","/content/drive/MyDrive/MGPR_2023\n","no. of files: 1 \n","\n","merged_df 379511\n","\n"," 60 Basti 2023\n","/content/drive/MyDrive/MGPR_2023\n","no. of files: 1 \n","\n","merged_df 1041620\n","\n"," 61 Chandauli 2023\n","/content/drive/MyDrive/MGPR_2023\n","no. of files: 1 \n","\n","merged_df 1108648\n","\n"," 62 Deoria 2023\n","/content/drive/MyDrive/MGPR_2023\n","no. of files: 1 \n","\n","merged_df 686270\n","\n"," 63 Faizabad 2023\n","/content/drive/MyDrive/MGPR_2023\n","no. of files: 1 \n","\n","merged_df 1132587\n","\n"," 64 Ghazipur 2023\n","/content/drive/MyDrive/MGPR_2023\n","no. of files: 1 \n","\n","merged_df 708669\n","\n"," 65 Gonda 2023\n","/content/drive/MyDrive/MGPR_2023\n","no. of files: 1 \n","\n","merged_df 2413059\n","\n"," 66 Gorakhpur 2023\n","/content/drive/MyDrive/MGPR_2023\n","no. of files: 1 \n","\n","merged_df 713471\n","\n"," 67 Jaunpur 2023\n","/content/drive/MyDrive/MGPR_2023\n","no. of files: 1 \n","\n","merged_df 1373721\n","\n"," 68 Kushinagar 2023\n","/content/drive/MyDrive/MGPR_2023\n","no. of files: 1 \n","\n","merged_df 1938508\n","\n"," 69 Lakhimpur Kheri 2023\n","/content/drive/MyDrive/MGPR_2023\n","no. of files: 1 \n","\n","merged_df 417\n","\n"," 70 Maharajganj 2023\n","/content/drive/MyDrive/MGPR_2023\n","no. of files: 1 \n","\n","merged_df 1461027\n","\n"," 71 Mau 2023\n","/content/drive/MyDrive/MGPR_2023\n","no. of files: 1 \n","\n","merged_df 535649\n","\n"," 72 Mirzapur 2023\n","/content/drive/MyDrive/MGPR_2023\n","no. of files: 1 \n","\n","merged_df 1452187\n","\n"," 73 Pratapgarhup 2023\n","/content/drive/MyDrive/MGPR_2023\n","no. of files: 1 \n","\n","merged_df 560\n","\n"," 74 Sant Kabir Nagar 2023\n","/content/drive/MyDrive/MGPR_2023\n","no. of files: 1 \n","\n","merged_df 333709\n","\n"," 75 Sant Ravi Das Nagar 2023\n","/content/drive/MyDrive/MGPR_2023\n","no. of files: 1 \n","\n","merged_df 300748\n","\n"," 76 Shravasti 2023\n","/content/drive/MyDrive/MGPR_2023\n","no. of files: 1 \n","\n","merged_df 1667482\n","\n"," 77 Siddharth Nagar 2023\n","/content/drive/MyDrive/MGPR_2023\n","no. of files: 1 \n","\n","merged_df 780608\n","\n"," 78 Sitapur 2023\n","/content/drive/MyDrive/MGPR_2023\n","no. of files: 1 \n","\n","merged_df 40\n","\n"," 79 Sonbhadra 2023\n","/content/drive/MyDrive/MGPR_2023\n","no. of files: 1 \n","\n","merged_df 5561742\n","\n"," 80 Sultanpur 2023\n","/content/drive/MyDrive/MGPR_2023\n","no. of files: 1 \n","\n","merged_df 15874\n","\n"," 81 Varanasi 2023\n","/content/drive/MyDrive/MGPR_2023\n","no. of files: 1 \n","\n","merged_df 464081\n","\n"," 82 Darjiling 2023\n","/content/drive/MyDrive/MGPR_2023\n","no. of files: 1 \n","\n","merged_df 15156\n","\n"," 83 Maldah 2023\n","/content/drive/MyDrive/MGPR_2023\n","no. of files: 1 \n","\n","merged_df 18893\n","\n"," 84 Murshidabad 2023\n","/content/drive/MyDrive/MGPR_2023\n","no. of files: 1 \n","\n","merged_df 3990\n","\n"," 85 Uttar Dinajpur 2023\n","/content/drive/MyDrive/MGPR_2023\n","no. of files: 1 \n","\n","merged_df 43326\n"]}],"source":["for year in years:\n"," dist_num = 0\n"," for district in dist_list:\n"," # if dist_num < 53:\n"," # dist_num += 1\n"," # continue\n"," print('\\n', dist_num, district, year)\n"," # dist_data_path = f'/content/drive/MyDrive/{agroclimaticZone_acronym_dict[agroclimatic_zone]}_{district}_{year}/'\n"," dist_data_path = f'/content/drive/MyDrive/{agroclimaticZone_acronym_dict[agroclimatic_zone]}_{year}'\n"," print(dist_data_path)\n"," # files = glob(dist_data_path + \"*.csv\")\n"," files = glob(f\"{dist_data_path}/{district}_{year}_all_grids.csv\")\n"," print(\"no. of files:\", len(files), '\\n')\n"," merged_df = pd.DataFrame()\n"," for fileName in files:\n"," df = pipeline(fileName, chunksize=100000)\n"," merged_df = pd.concat([merged_df, df], ignore_index=True)\n"," print(\"merged_df\", len(merged_df))\n"," merged_df.to_csv(f'/content/drive/MyDrive/TreeHealth/{agroclimatic_zone}/{district}/{year}/result_monthly_cc.csv', index=False)\n"," dist_num += 1"]}],"metadata":{"colab":{"collapsed_sections":["vkgtoxewcscO"],"provenance":[{"file_id":"1EZVgV-JFERHxOKGgBNKweDaXnmAGoo83","timestamp":1758129019932}]},"kernelspec":{"display_name":"Python 3","name":"python3"},"language_info":{"name":"python"}},"nbformat":4,"nbformat_minor":0} \ No newline at end of file diff --git a/utilities/scripts/tree_health/colab_notebooks/trees_corrections.ipynb b/utilities/scripts/tree_health/colab_notebooks/trees_corrections.ipynb new file mode 100644 index 00000000..ef71a618 --- /dev/null +++ b/utilities/scripts/tree_health/colab_notebooks/trees_corrections.ipynb @@ -0,0 +1 @@ +{"cells":[{"cell_type":"code","execution_count":1,"metadata":{"executionInfo":{"elapsed":5831,"status":"ok","timestamp":1774795294300,"user":{"displayName":"CoRE Stack","userId":"15795073488475210764"},"user_tz":-330},"id":"IYRi4f4mL668"},"outputs":[],"source":["import pandas as pd\n","import joblib\n","import time\n","from glob import glob\n","import numpy as np\n","import statistics as st\n","import matplotlib.pyplot as plt\n","import ee\n","import ast"]},{"cell_type":"code","execution_count":2,"metadata":{"colab":{"base_uri":"https://localhost:8080/"},"executionInfo":{"elapsed":27519,"status":"ok","timestamp":1774795330023,"user":{"displayName":"CoRE Stack","userId":"15795073488475210764"},"user_tz":-330},"id":"qvEjcCWgLuTg","outputId":"4a74a17d-e4cc-4cc5-ebc0-286d81d6035e"},"outputs":[{"output_type":"stream","name":"stdout","text":["Mounted at /content/drive\n"]}],"source":["from google.colab import drive\n","drive.mount('/content/drive')"]},{"cell_type":"code","execution_count":3,"metadata":{"executionInfo":{"elapsed":15,"status":"ok","timestamp":1774795330024,"user":{"displayName":"CoRE Stack","userId":"15795073488475210764"},"user_tz":-330},"id":"fORWmS6aL-gR"},"outputs":[],"source":["acz_list = [\n"," 'Western Himalayan Region',\n"," # 'Eastern Himalayan Region',\n"," # 'Lower Gangetic Plain Region',\n"," # 'Middle Gangetic Plain Region',\n"," # 'Upper Gangetic Plain Region',\n"," # 'Trans Gangetic Plain Region',\n"," # 'Eastern Plateau & Hills Region',\n"," # 'Central Plateau & Hills Region',\n"," # 'Western Plateau and Hills Region',\n"," # 'Southern Plateau and Hills Region',\n"," # 'East Coast Plains & Hills Region'\n"," ]"]},{"cell_type":"code","execution_count":4,"metadata":{"executionInfo":{"elapsed":6,"status":"ok","timestamp":1774795342043,"user":{"displayName":"CoRE Stack","userId":"15795073488475210764"},"user_tz":-330},"id":"6Fiwos-UMDhQ"},"outputs":[],"source":["best_month_dict = {'Eastern Plateau & Hills Region': 'cc_12',\n"," 'Middle Gangetic Plain Region': 'cc_10',\n"," 'Lower Gangetic Plain Region': 'cc_9',\n"," 'Western Himalayan Region': 'cc_8',\n"," 'Eastern Himalayan Region': 'cc_10',\n"," 'Upper Gangetic Plain Region': 'cc_9',\n"," 'Trans Gangetic Plain Region': 'cc_9',\n"," 'Central Plateau & Hills Region': 'cc_7',\n"," 'Western Plateau and Hills Region': 'cc_11',\n"," 'Southern Plateau and Hills Region': 'cc_8',\n"," 'East Coast Plains & Hills Region': 'cc_12'}"]},{"cell_type":"code","execution_count":18,"metadata":{"executionInfo":{"elapsed":47,"status":"ok","timestamp":1774807519586,"user":{"displayName":"CoRE Stack","userId":"15795073488475210764"},"user_tz":-330},"id":"5T_YvOd-MK1m"},"outputs":[],"source":["# if AY 2023's data is being added, set year = '2023'\n","# No correction for 2016, starts from 2017\n","# (For correcting 2017, set to year to 2019)\n","year = '2024'\n","\n","# We need to correct data from year_2 and year_1\n","year_1 = int(year)-1\n","year_2 = int(year)-2\n","year_3 = int(year)-3\n","year_4 = int(year)-4"]},{"cell_type":"code","execution_count":7,"metadata":{"colab":{"base_uri":"https://localhost:8080/"},"executionInfo":{"elapsed":1284,"status":"ok","timestamp":1774715945110,"user":{"displayName":"CoRE Stack","userId":"15795073488475210764"},"user_tz":-330},"id":"3qECVxwWVRAP","outputId":"20eaa902-dbb3-4630-fc49-85b41ca7d54b"},"outputs":[{"output_type":"stream","name":"stdout","text":["2\n","dist_list: ['Katihar', 'Kishanganj']\n"]}],"source":["# df = pd.read_csv(f'drive/MyDrive/TreeHealth/Agroclimatic_regions/{acz_list[0]}.csv')\n","# dist_list = list(df['Name'])\n","# dist_list = ['Katihar', 'Kishanganj']\n","# print(len(dist_list))\n","# print(f'dist_list: {dist_list}')"]},{"cell_type":"markdown","metadata":{"id":"OpBWXM1TMVAs"},"source":["# Data Correction - CCD"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"pNuZ_qZtMWEj"},"outputs":[],"source":["def ccd_corrections_2017(df):\n"," columns = list(df.columns)\n"," n = len(columns)\n","\n"," # In case the no. of columns are too less for any corrections to be performed\n"," if n < 5:\n"," print(f\"Number of columns are {n}, which is too less to perform any corrections..\")\n"," correction_df = pd.DataFrame(columns=columns)\n"," return correction_df\n","\n"," # Correcting the second (2017) year\n"," correction_df = df[(df[columns[n-1]] == df[columns[n-2]]) & (df[columns[n-2]] == df[columns[n-4]]) & (df[columns[n-3]] !=df[columns[n-1]])]\n"," correction_df.drop_duplicates(inplace=True)\n","\n"," # Actually Performing the corrections once all rows where corrections need to be performed are found\n"," correction_df.loc[(correction_df[columns[n-1]] == correction_df[columns[n-2]]) &\n"," (correction_df[columns[n-2]] == correction_df[columns[n-4]]) &\n"," (correction_df[columns[n-3]] != correction_df[columns[n-1]]), columns[n-3]] = correction_df[columns[n-1]]\n","\n"," return correction_df\n","\n","\n","def corrections(df):\n"," columns = list(df.columns)\n"," n = len(columns)\n","\n"," # In case the no. of columns are too less for any corrections to be performed\n"," if n < 5:\n"," correction_df = pd.DataFrame(columns=columns)\n"," return correction_df\n","\n"," # Correcting the second last year\n"," correction_df = df[(df[columns[n-1]] == df[columns[n-3]]) & (df[columns[n-3]] == df[columns[n-4]]) & (df[columns[n-2]] != df[columns[n-1]])]\n"," correction_df.drop_duplicates(inplace=True)\n"," print(\"correction_df 1\", correction_df)\n"," # Correcting the middle year\n"," new_df = df[(df[columns[n-5]] == df[columns[n-4]]) & (df[columns[n-4]] == df[columns[n-2]]) & (df[columns[n-2]] == df[columns[n-1]]) & (df[columns[n-3]] != df[columns[n-5]])]\n"," correction_df = pd.concat([correction_df, new_df], ignore_index=True)\n"," correction_df.drop_duplicates(inplace=True)\n"," del(new_df)\n"," print(\"correction_df 2\", correction_df)\n"," # Actually Performing the corrections once all rows where corrections need to be performed are found\n"," correction_df.loc[(correction_df[columns[n-1]] == correction_df[columns[n-3]]) & (correction_df[columns[n-3]] == correction_df[columns[n-4]]) &\n"," (correction_df[columns[n-2]] != correction_df[columns[n-1]]), columns[n-2]] = correction_df[columns[n-1]]\n"," correction_df.loc[(correction_df[columns[n-5]] == correction_df[columns[n-4]]) & (correction_df[columns[n-4]] == correction_df[columns[n-2]]) &\n"," (correction_df[columns[n-2]] == correction_df[columns[n-1]]) & (correction_df[columns[n-3]] != correction_df[columns[n-5]]), columns[n-3]] = correction_df[columns[n-5]]\n","\n"," return correction_df"]},{"cell_type":"code","execution_count":null,"metadata":{"colab":{"base_uri":"https://localhost:8080/","height":517},"executionInfo":{"elapsed":20422,"status":"error","timestamp":1771511098161,"user":{"displayName":"CoRE Stack","userId":"15795073488475210764"},"user_tz":-330},"id":"kZsJ0sCEMcQo","outputId":"e3b8da73-1c52-49c8-ea41-db204f58acc8"},"outputs":[{"name":"stdout","output_type":"stream","text":["East Coast Plains & Hills Region\n","len(dist_list): 68\n","0 Chittoor\n"]},{"ename":"KeyboardInterrupt","evalue":"","output_type":"error","traceback":["\u001b[0;31m---------------------------------------------------------------------------\u001b[0m","\u001b[0;31mKeyboardInterrupt\u001b[0m Traceback (most recent call last)","\u001b[0;32m/tmp/ipython-input-50941476.py\u001b[0m in \u001b[0;36m\u001b[0;34m()\u001b[0m\n\u001b[1;32m 36\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 37\u001b[0m \u001b[0;32mtry\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 38\u001b[0;31m \u001b[0mdf_3\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mpd\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mread_csv\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mfile_3\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 39\u001b[0m \u001b[0mdf_3\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mdrop\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mcolumns\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mcolumnList\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0minplace\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;32mTrue\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 40\u001b[0m \u001b[0mdf_3\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mrename\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mcolumns\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;34m{\u001b[0m\u001b[0mband\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0;34m'cover_class'\u001b[0m\u001b[0;34m}\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0minplace\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;32mTrue\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n","\u001b[0;32m/usr/local/lib/python3.12/dist-packages/pandas/io/parsers/readers.py\u001b[0m in \u001b[0;36mread_csv\u001b[0;34m(filepath_or_buffer, sep, delimiter, header, names, index_col, usecols, dtype, engine, converters, true_values, false_values, skipinitialspace, skiprows, skipfooter, nrows, na_values, keep_default_na, na_filter, verbose, skip_blank_lines, parse_dates, infer_datetime_format, keep_date_col, date_parser, date_format, dayfirst, cache_dates, iterator, chunksize, compression, thousands, decimal, lineterminator, quotechar, quoting, doublequote, escapechar, comment, encoding, encoding_errors, dialect, on_bad_lines, delim_whitespace, low_memory, memory_map, float_precision, storage_options, dtype_backend)\u001b[0m\n\u001b[1;32m 1024\u001b[0m \u001b[0mkwds\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mupdate\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mkwds_defaults\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1025\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m-> 1026\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0m_read\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mfilepath_or_buffer\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mkwds\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 1027\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1028\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n","\u001b[0;32m/usr/local/lib/python3.12/dist-packages/pandas/io/parsers/readers.py\u001b[0m in \u001b[0;36m_read\u001b[0;34m(filepath_or_buffer, kwds)\u001b[0m\n\u001b[1;32m 624\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 625\u001b[0m \u001b[0;32mwith\u001b[0m \u001b[0mparser\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 626\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0mparser\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mread\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mnrows\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 627\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 628\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n","\u001b[0;32m/usr/local/lib/python3.12/dist-packages/pandas/io/parsers/readers.py\u001b[0m in \u001b[0;36mread\u001b[0;34m(self, nrows)\u001b[0m\n\u001b[1;32m 1921\u001b[0m \u001b[0mcolumns\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1922\u001b[0m \u001b[0mcol_dict\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m-> 1923\u001b[0;31m \u001b[0;34m)\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_engine\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mread\u001b[0m\u001b[0;34m(\u001b[0m \u001b[0;31m# type: ignore[attr-defined]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 1924\u001b[0m \u001b[0mnrows\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1925\u001b[0m )\n","\u001b[0;32m/usr/local/lib/python3.12/dist-packages/pandas/io/parsers/c_parser_wrapper.py\u001b[0m in \u001b[0;36mread\u001b[0;34m(self, nrows)\u001b[0m\n\u001b[1;32m 232\u001b[0m \u001b[0;32mtry\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 233\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mlow_memory\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 234\u001b[0;31m \u001b[0mchunks\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_reader\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mread_low_memory\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mnrows\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 235\u001b[0m \u001b[0;31m# destructive to chunks\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 236\u001b[0m \u001b[0mdata\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0m_concatenate_chunks\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mchunks\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n","\u001b[0;32mparsers.pyx\u001b[0m in \u001b[0;36mpandas._libs.parsers.TextReader.read_low_memory\u001b[0;34m()\u001b[0m\n","\u001b[0;32mparsers.pyx\u001b[0m in \u001b[0;36mpandas._libs.parsers.TextReader._read_rows\u001b[0;34m()\u001b[0m\n","\u001b[0;32mparsers.pyx\u001b[0m in \u001b[0;36mpandas._libs.parsers.TextReader._tokenize_rows\u001b[0;34m()\u001b[0m\n","\u001b[0;32mparsers.pyx\u001b[0m in \u001b[0;36mpandas._libs.parsers.TextReader._check_tokenize_status\u001b[0;34m()\u001b[0m\n","\u001b[0;32mparsers.pyx\u001b[0m in \u001b[0;36mpandas._libs.parsers.raise_parser_error\u001b[0;34m()\u001b[0m\n","\u001b[0;32m/usr/lib/python3.12/codecs.py\u001b[0m in \u001b[0;36mdecode\u001b[0;34m(self, input, final)\u001b[0m\n","\u001b[0;31mKeyboardInterrupt\u001b[0m: "]}],"source":["for agroclimatic_zone in acz_list:\n"," print(agroclimatic_zone)\n"," df = pd.read_csv(f'drive/MyDrive/TreeHealth/Agroclimatic_regions/{agroclimatic_zone}.csv')\n"," dist_list = list(df['Name'])\n"," print(f'len(dist_list): {len(dist_list)}')\n","\n"," path = f'drive/MyDrive/TreeHealth/{agroclimatic_zone}/'\n","\n"," total_corrections = 0\n"," total_length = 0\n","\n"," for i in range(len(dist_list)):\n","\n"," # if i != 11:\n"," # continue\n","\n"," print(i, dist_list[i])\n"," file_4 = path + dist_list[i] + f\"/{year_4}/result_monthly_cc.csv\"\n"," file_3 = path + dist_list[i] + f\"/{year_3}/result_monthly_cc.csv\"\n"," file_2 = path + dist_list[i] + f\"/{year_2}/result_monthly_cc.csv\"\n"," file_1 = path + dist_list[i] + f\"/{year_1}/result_monthly_cc.csv\"\n"," file_0 = path + dist_list[i] + f\"/{year}/result_monthly_cc.csv\"\n","\n"," band = best_month_dict[agroclimatic_zone]\n"," columnList = ['cc_1', 'cc_2', 'cc_3', 'cc_4', 'cc_5', 'cc_6', 'cc_7', 'cc_8', 'cc_9', 'cc_10', 'cc_11', 'cc_12']\n"," columnList.remove(band)\n","\n"," try:\n"," df_4 = pd.read_csv(file_4)\n"," df_4.drop(columns=columnList, inplace=True)\n"," df_4.rename(columns={band: 'cover_class'}, inplace=True)\n","\n"," except Exception as e:\n"," print(e)\n"," df_4 = pd.DataFrame(columns=['.geo', 'cover_class'])\n","\n"," try:\n"," df_3 = pd.read_csv(file_3)\n"," df_3.drop(columns=columnList, inplace=True)\n"," df_3.rename(columns={band: 'cover_class'}, inplace=True)\n","\n"," except Exception as e:\n"," print(e)\n"," df_3 = pd.DataFrame(columns=['.geo', 'cover_class'])\n","\n"," df_4.rename(columns={'cover_class': f'cc_{year_4}'}, inplace=True)\n"," df_3.rename(columns={'cover_class': f'cc_{year_3}'}, inplace=True)\n"," merged_df = pd.merge(df_4, df_3, on='.geo', how='outer')\n"," del(df_4)\n"," del(df_3)\n","\n"," try:\n"," df_2 = pd.read_csv(file_2)\n"," df_2.drop(columns=columnList, inplace=True)\n"," df_2.rename(columns={band: 'cover_class'}, inplace=True)\n","\n"," except Exception as e:\n"," print(e)\n"," df_2 = pd.DataFrame(columns=['.geo', 'cover_class'])\n","\n"," df_2.rename(columns={'cover_class': f'cc_{year_2}'}, inplace=True)\n"," merged_df = pd.merge(merged_df, df_2, on='.geo', how='outer')\n"," del(df_2)\n","\n"," try:\n"," df_1 = pd.read_csv(file_1)\n"," df_1.drop(columns=columnList, inplace=True)\n"," df_1.rename(columns={band: 'cover_class'}, inplace=True)\n"," except Exception as e:\n"," print(e)\n"," df_1 = pd.DataFrame(columns=['.geo', 'cover_class'])\n","\n"," df_1.rename(columns={'cover_class': f'cc_{year_1}'}, inplace=True)\n"," merged_df = pd.merge(merged_df, df_1, on='.geo', how='outer')\n"," del(df_1)\n","\n"," try:\n"," df_0 = pd.read_csv(file_0)\n"," df_0.drop(columns=columnList, inplace=True)\n"," df_0.rename(columns={band: 'cover_class'}, inplace=True)\n","\n"," except Exception as e:\n"," print(e)\n"," df_0 = pd.DataFrame(columns=['.geo', 'cover_class'])\n","\n"," df_0.rename(columns={'cover_class': f'cc_{year}'}, inplace=True)\n"," merged_df = pd.merge(merged_df, df_0, on='.geo', how='outer')\n"," del(df_0)\n","\n","\n"," merged_df = merged_df[['.geo', f'cc_{year_4}', f'cc_{year_3}', f'cc_{year_2}', f'cc_{year_1}', f'cc_{year}']]\n"," print(merged_df)\n"," total_length += len(merged_df)\n"," print(\"Length of merged_df:\", len(merged_df))\n"," print(\"Total Length:\", total_length)\n"," if year == '2019':\n"," print(\"Correcting 2017\")\n"," correction_df = ccd_corrections_2017(merged_df)\n"," else:\n"," correction_df = corrections(merged_df)\n","\n"," del(merged_df)\n"," total_corrections += len(correction_df)\n"," print(f'Correction_df length: {len(correction_df)}')\n"," print(f'Total Corrections: {total_corrections}')\n"," if len(correction_df) > 0:\n"," # if year != last_year:\n"," correction_year_2 = correction_df[['.geo', f'cc_{year_2}']]\n"," correction_year_2.to_csv(f'{path}{dist_list[i]}/{year_2}/result_monthly_cc_corrections.csv', index=False)\n"," if year != '2019':\n"," correction_year_1 = correction_df[['.geo', f'cc_{year_1}']]\n"," correction_year_1.to_csv(f'{path}{dist_list[i]}/{year_1}/result_monthly_cc_corrections.csv', index=False) # Comment when correcting 2017\n","\n"," del(correction_df)"]},{"cell_type":"markdown","metadata":{"id":"Tw7xfx5BO1eH"},"source":[" # Data Correction - CH"]},{"cell_type":"code","execution_count":6,"metadata":{"executionInfo":{"elapsed":20,"status":"ok","timestamp":1774795365410,"user":{"displayName":"CoRE Stack","userId":"15795073488475210764"},"user_tz":-330},"id":"qqU0JIe4O40i"},"outputs":[],"source":["# Column Order\n","# ['.geo',\n","# 'rh98_{year_4}', 'rh98_{year_3}', 'rh98_{year_2}', 'rh98_{year_1}', 'rh98_{year}',\n","# 'rh75_{year_4}', 'rh75_{year_3}', 'rh75_{year_2}', 'rh75_{year_1}', 'rh75_{year}',\n","# 'rh50_{year_4}', 'rh50_{year_3}', 'rh50_{year_2}', 'rh50_{year_1}', 'rh50_{year}']\n","\n","\n","def ch_corrections(df):\n"," columns = list(df.columns)\n"," n = len(columns)\n","\n"," # Correcting the second last year - rh98\n"," correction_df = df[(df[columns[5]] == df[columns[3]]) & (df[columns[3]] == df[columns[2]]) & (df[columns[4]] != df[columns[5]])]\n"," correction_df.drop_duplicates(inplace=True)\n","\n"," # Correcting the middle year - rh98\n"," new_df = df[(df[columns[1]] == df[columns[2]]) & (df[columns[2]] == df[columns[4]]) & (df[columns[4]] == df[columns[5]]) & (df[columns[3]] != df[columns[1]])]\n"," correction_df = pd.concat([correction_df, new_df], ignore_index=True)\n"," correction_df.drop_duplicates(inplace=True)\n"," del(new_df)\n","\n","\n"," # Correcting the second last year - rh75\n"," new_df = df[(df[columns[10]] == df[columns[8]]) & (df[columns[8]] == df[columns[7]]) & (df[columns[9]] != df[columns[10]])]\n"," correction_df = pd.concat([correction_df, new_df], ignore_index=True)\n"," del(new_df)\n"," correction_df.drop_duplicates(inplace=True)\n","\n"," # Correcting the middle year - rh75\n"," new_df = df[(df[columns[6]] == df[columns[7]]) & (df[columns[7]] == df[columns[9]]) & (df[columns[9]] == df[columns[10]]) & (df[columns[8]] != df[columns[6]])]\n"," correction_df = pd.concat([correction_df, new_df], ignore_index=True)\n"," correction_df.drop_duplicates(inplace=True)\n"," del(new_df)\n","\n","\n"," # Correcting the second last year - rh50\n"," new_df = df[(df[columns[15]] == df[columns[13]]) & (df[columns[13]] == df[columns[12]]) & (df[columns[14]] != df[columns[15]])]\n"," correction_df = pd.concat([correction_df, new_df], ignore_index=True)\n"," del(new_df)\n"," correction_df.drop_duplicates(inplace=True)\n","\n"," # Correcting the middle year - rh50\n"," new_df = df[(df[columns[11]] == df[columns[12]]) & (df[columns[12]] == df[columns[14]]) & (df[columns[14]] == df[columns[15]]) & (df[columns[13]] != df[columns[11]])]\n"," correction_df = pd.concat([correction_df, new_df], ignore_index=True)\n"," correction_df.drop_duplicates(inplace=True)\n"," del(new_df)\n","\n","\n"," # Actually Performing the corrections once all rows where corrections need to be performed are found\n","\n"," # rh98\n"," correction_df.loc[(correction_df[columns[5]] == correction_df[columns[3]]) & (correction_df[columns[3]] == correction_df[columns[2]]) &\n"," (correction_df[columns[4]] != correction_df[columns[5]]), columns[4]] = correction_df[columns[5]]\n"," correction_df.loc[(correction_df[columns[1]] == correction_df[columns[2]]) & (correction_df[columns[2]] == correction_df[columns[4]]) &\n"," (correction_df[columns[4]] == correction_df[columns[5]]) & (correction_df[columns[3]] != correction_df[columns[1]]), columns[3]] = correction_df[columns[1]]\n","\n","\n"," # rh75\n"," correction_df.loc[(correction_df[columns[10]] == correction_df[columns[8]]) & (correction_df[columns[8]] == correction_df[columns[7]]) &\n"," (correction_df[columns[9]] != correction_df[columns[10]]), columns[9]] = correction_df[columns[10]]\n"," correction_df.loc[(correction_df[columns[6]] == correction_df[columns[7]]) & (correction_df[columns[7]] == correction_df[columns[9]]) &\n"," (correction_df[columns[9]] == correction_df[columns[10]]) & (correction_df[columns[8]] != correction_df[columns[6]]), columns[8]] = correction_df[columns[6]]\n","\n","\n"," # rh50\n"," correction_df.loc[(correction_df[columns[15]] == correction_df[columns[13]]) & (correction_df[columns[13]] == correction_df[columns[12]]) &\n"," (correction_df[columns[14]] != correction_df[columns[15]]), columns[14]] = correction_df[columns[15]]\n"," correction_df.loc[(correction_df[columns[11]] == correction_df[columns[12]]) & (correction_df[columns[12]] == correction_df[columns[14]]) &\n"," (correction_df[columns[14]] == correction_df[columns[15]]) & (correction_df[columns[13]] != correction_df[columns[11]]), columns[13]] = correction_df[columns[11]]\n","\n"," return correction_df\n","\n","def ch_corrections_2017(df):\n"," columns = list(df.columns)\n"," n = len(columns)\n","\n"," # Correcting the second last year - rh98\n"," correction_df = df[(df[columns[5]] == df[columns[4]]) & (df[columns[4]] == df[columns[2]]) & (df[columns[3]] != df[columns[5]])]\n"," correction_df.drop_duplicates(inplace=True)\n","\n"," # Correcting the second last year - rh75\n"," new_df = df[(df[columns[10]] == df[columns[9]]) & (df[columns[9]] == df[columns[7]]) & (df[columns[8]] != df[columns[10]])]\n"," correction_df = pd.concat([correction_df, new_df], ignore_index=True)\n"," del(new_df)\n"," correction_df.drop_duplicates(inplace=True)\n","\n"," # Correcting the second last year - rh50\n"," new_df = df[(df[columns[15]] == df[columns[14]]) & (df[columns[14]] == df[columns[12]]) & (df[columns[13]] != df[columns[15]])]\n"," correction_df = pd.concat([correction_df, new_df], ignore_index=True)\n"," del(new_df)\n"," correction_df.drop_duplicates(inplace=True)\n","\n"," # Actually Performing the corrections once all rows where corrections need to be performed are found\n","\n"," # rh98\n"," correction_df.loc[(correction_df[columns[5]] == correction_df[columns[4]]) & (correction_df[columns[4]] == correction_df[columns[2]]) &\n"," (correction_df[columns[3]] != correction_df[columns[5]]), columns[3]] = correction_df[columns[5]]\n","\n"," # rh75\n"," correction_df.loc[(correction_df[columns[10]] == correction_df[columns[9]]) & (correction_df[columns[9]] == correction_df[columns[7]]) &\n"," (correction_df[columns[8]] != correction_df[columns[10]]), columns[8]] = correction_df[columns[10]]\n","\n"," # rh50\n"," correction_df.loc[(correction_df[columns[15]] == correction_df[columns[14]]) & (correction_df[columns[14]] == correction_df[columns[12]]) &\n"," (correction_df[columns[13]] != correction_df[columns[15]]), columns[13]] = correction_df[columns[15]]\n","\n"," return correction_df"]},{"cell_type":"markdown","metadata":{"id":"2aOG9olEVlV-"},"source":["CH corrections without Chunking"]},{"cell_type":"code","execution_count":19,"metadata":{"colab":{"base_uri":"https://localhost:8080/"},"id":"exmkYL5zPGs-","outputId":"3c535796-d3f1-468c-de0e-a1ba907ce127","executionInfo":{"status":"ok","timestamp":1774808836553,"user_tz":-330,"elapsed":1310782,"user":{"displayName":"CoRE Stack","userId":"15795073488475210764"}}},"outputs":[{"output_type":"stream","name":"stdout","text":["Western Himalayan Region\n","len(dist_list): 4\n","dist_list=: ['Dehradun', 'Garhwal', 'Nainital', 'Champawat']\n","0 Dehradun\n","merged_df>>> .geo rh98_2020 \\\n","0 {\"geodesic\":false,\"type\":\"Point\",\"coordinates\"... 0.0 \n","1 {\"geodesic\":false,\"type\":\"Point\",\"coordinates\"... 0.0 \n","2 {\"geodesic\":false,\"type\":\"Point\",\"coordinates\"... 0.0 \n","3 {\"geodesic\":false,\"type\":\"Point\",\"coordinates\"... 0.0 \n","4 {\"geodesic\":false,\"type\":\"Point\",\"coordinates\"... 0.0 \n","... ... ... \n","5126057 {\"geodesic\":false,\"type\":\"Point\",\"coordinates\"... 0.0 \n","5126058 {\"geodesic\":false,\"type\":\"Point\",\"coordinates\"... 0.0 \n","5126059 {\"geodesic\":false,\"type\":\"Point\",\"coordinates\"... 0.0 \n","5126060 {\"geodesic\":false,\"type\":\"Point\",\"coordinates\"... 0.0 \n","5126061 {\"geodesic\":false,\"type\":\"Point\",\"coordinates\"... 0.0 \n","\n"," rh75_2020 rh50_2020 rh98_2021 rh75_2021 rh50_2021 rh98_2022 \\\n","0 0.0 0.0 0.0 0.0 0.0 0.0 \n","1 0.0 0.0 0.0 0.0 0.0 0.0 \n","2 0.0 0.0 0.0 0.0 1.0 0.0 \n","3 0.0 0.0 0.0 0.0 0.0 0.0 \n","4 0.0 0.0 0.0 0.0 0.0 0.0 \n","... ... ... ... ... ... ... \n","5126057 0.0 0.0 0.0 0.0 0.0 0.0 \n","5126058 0.0 0.0 0.0 0.0 0.0 0.0 \n","5126059 0.0 0.0 0.0 0.0 0.0 0.0 \n","5126060 0.0 0.0 0.0 0.0 0.0 0.0 \n","5126061 0.0 0.0 0.0 0.0 0.0 0.0 \n","\n"," rh75_2022 rh50_2022 rh98_2023 rh75_2023 rh50_2023 rh98_2024 \\\n","0 0.0 0.0 0.0 0.0 0.0 0.0 \n","1 0.0 0.0 0.0 0.0 0.0 0.0 \n","2 0.0 0.0 0.0 0.0 1.0 0.0 \n","3 0.0 0.0 0.0 0.0 0.0 0.0 \n","4 0.0 0.0 0.0 0.0 0.0 0.0 \n","... ... ... ... ... ... ... \n","5126057 0.0 0.0 0.0 0.0 0.0 0.0 \n","5126058 0.0 0.0 0.0 0.0 0.0 0.0 \n","5126059 0.0 0.0 0.0 0.0 0.0 0.0 \n","5126060 0.0 0.0 0.0 0.0 0.0 0.0 \n","5126061 0.0 0.0 0.0 0.0 0.0 0.0 \n","\n"," rh75_2024 rh50_2024 \n","0 0.0 0.0 \n","1 0.0 0.0 \n","2 0.0 0.0 \n","3 0.0 0.0 \n","4 0.0 0.0 \n","... ... ... \n","5126057 0.0 0.0 \n","5126058 1.0 0.0 \n","5126059 0.0 1.0 \n","5126060 0.0 1.0 \n","5126061 0.0 0.0 \n","\n","[5126062 rows x 16 columns]\n","Length of merged_df: 5126062\n","Total Length: 5126062\n"]},{"output_type":"stream","name":"stderr","text":["/tmp/ipykernel_17578/833223507.py:14: SettingWithCopyWarning: \n","A value is trying to be set on a copy of a slice from a DataFrame\n","\n","See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy\n"," correction_df.drop_duplicates(inplace=True)\n"]},{"output_type":"stream","name":"stdout","text":["Correction_df length: 1278553\n","Total Corrections: 1278553\n","1 Garhwal\n","merged_df>>> .geo rh98_2020 \\\n","0 {\"geodesic\":false,\"type\":\"Point\",\"coordinates\"... 0.0 \n","1 {\"geodesic\":false,\"type\":\"Point\",\"coordinates\"... 0.0 \n","2 {\"geodesic\":false,\"type\":\"Point\",\"coordinates\"... 0.0 \n","3 {\"geodesic\":false,\"type\":\"Point\",\"coordinates\"... 0.0 \n","4 {\"geodesic\":false,\"type\":\"Point\",\"coordinates\"... 0.0 \n","... ... ... \n","9596162 {\"geodesic\":false,\"type\":\"Point\",\"coordinates\"... 0.0 \n","9596163 {\"geodesic\":false,\"type\":\"Point\",\"coordinates\"... 0.0 \n","9596164 {\"geodesic\":false,\"type\":\"Point\",\"coordinates\"... 0.0 \n","9596165 {\"geodesic\":false,\"type\":\"Point\",\"coordinates\"... 0.0 \n","9596166 {\"geodesic\":false,\"type\":\"Point\",\"coordinates\"... 0.0 \n","\n"," rh75_2020 rh50_2020 rh98_2021 rh75_2021 rh50_2021 rh98_2022 \\\n","0 0.0 0.0 0.0 0.0 0.0 0.0 \n","1 0.0 0.0 0.0 0.0 0.0 0.0 \n","2 0.0 0.0 0.0 0.0 0.0 0.0 \n","3 0.0 0.0 0.0 0.0 0.0 0.0 \n","4 0.0 0.0 0.0 0.0 0.0 0.0 \n","... ... ... ... ... ... ... \n","9596162 0.0 0.0 0.0 0.0 0.0 NaN \n","9596163 0.0 0.0 0.0 0.0 0.0 0.0 \n","9596164 0.0 0.0 0.0 0.0 0.0 0.0 \n","9596165 0.0 0.0 0.0 0.0 0.0 0.0 \n","9596166 0.0 0.0 0.0 0.0 0.0 0.0 \n","\n"," rh75_2022 rh50_2022 rh98_2023 rh75_2023 rh50_2023 rh98_2024 \\\n","0 0.0 0.0 0.0 0.0 0.0 0.0 \n","1 0.0 0.0 0.0 0.0 0.0 0.0 \n","2 0.0 0.0 0.0 0.0 0.0 0.0 \n","3 0.0 0.0 0.0 0.0 0.0 0.0 \n","4 0.0 0.0 0.0 0.0 0.0 0.0 \n","... ... ... ... ... ... ... \n","9596162 NaN NaN NaN NaN NaN 0.0 \n","9596163 0.0 0.0 NaN NaN NaN 0.0 \n","9596164 0.0 0.0 0.0 0.0 0.0 0.0 \n","9596165 0.0 0.0 NaN NaN NaN 0.0 \n","9596166 0.0 0.0 0.0 0.0 0.0 0.0 \n","\n"," rh75_2024 rh50_2024 \n","0 0.0 0.0 \n","1 0.0 0.0 \n","2 0.0 0.0 \n","3 0.0 0.0 \n","4 0.0 0.0 \n","... ... ... \n","9596162 0.0 0.0 \n","9596163 0.0 0.0 \n","9596164 0.0 0.0 \n","9596165 0.0 0.0 \n","9596166 0.0 0.0 \n","\n","[9596167 rows x 16 columns]\n","Length of merged_df: 9596167\n","Total Length: 14722229\n"]},{"output_type":"stream","name":"stderr","text":["/tmp/ipykernel_17578/833223507.py:14: SettingWithCopyWarning: \n","A value is trying to be set on a copy of a slice from a DataFrame\n","\n","See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy\n"," correction_df.drop_duplicates(inplace=True)\n"]},{"output_type":"stream","name":"stdout","text":["Correction_df length: 1725432\n","Total Corrections: 3003985\n","2 Nainital\n","merged_df>>> .geo rh98_2020 \\\n","0 {\"geodesic\":false,\"type\":\"Point\",\"coordinates\"... 0.0 \n","1 {\"geodesic\":false,\"type\":\"Point\",\"coordinates\"... 0.0 \n","2 {\"geodesic\":false,\"type\":\"Point\",\"coordinates\"... 0.0 \n","3 {\"geodesic\":false,\"type\":\"Point\",\"coordinates\"... 0.0 \n","4 {\"geodesic\":false,\"type\":\"Point\",\"coordinates\"... 0.0 \n","... ... ... \n","6912865 {\"geodesic\":false,\"type\":\"Point\",\"coordinates\"... 1.0 \n","6912866 {\"geodesic\":false,\"type\":\"Point\",\"coordinates\"... 1.0 \n","6912867 {\"geodesic\":false,\"type\":\"Point\",\"coordinates\"... 1.0 \n","6912868 {\"geodesic\":false,\"type\":\"Point\",\"coordinates\"... 1.0 \n","6912869 {\"geodesic\":false,\"type\":\"Point\",\"coordinates\"... 1.0 \n","\n"," rh75_2020 rh50_2020 rh98_2021 rh75_2021 rh50_2021 rh98_2022 \\\n","0 1.0 1.0 0.0 0.0 0.0 0.0 \n","1 0.0 0.0 0.0 0.0 0.0 0.0 \n","2 0.0 0.0 0.0 0.0 0.0 0.0 \n","3 0.0 0.0 0.0 0.0 0.0 0.0 \n","4 0.0 0.0 0.0 0.0 0.0 0.0 \n","... ... ... ... ... ... ... \n","6912865 1.0 1.0 1.0 1.0 1.0 0.0 \n","6912866 1.0 1.0 1.0 1.0 1.0 0.0 \n","6912867 1.0 1.0 1.0 1.0 1.0 1.0 \n","6912868 1.0 1.0 1.0 1.0 1.0 1.0 \n","6912869 1.0 1.0 0.0 1.0 1.0 0.0 \n","\n"," rh75_2022 rh50_2022 rh98_2023 rh75_2023 rh50_2023 rh98_2024 \\\n","0 0.0 0.0 0.0 0.0 0.0 0.0 \n","1 0.0 0.0 0.0 0.0 0.0 0.0 \n","2 0.0 0.0 0.0 0.0 0.0 0.0 \n","3 0.0 0.0 0.0 0.0 0.0 0.0 \n","4 0.0 0.0 0.0 0.0 0.0 0.0 \n","... ... ... ... ... ... ... \n","6912865 1.0 1.0 1.0 1.0 1.0 0.0 \n","6912866 1.0 0.0 1.0 1.0 1.0 0.0 \n","6912867 1.0 1.0 1.0 1.0 1.0 0.0 \n","6912868 1.0 1.0 1.0 1.0 1.0 0.0 \n","6912869 1.0 1.0 1.0 1.0 1.0 0.0 \n","\n"," rh75_2024 rh50_2024 \n","0 0.0 0.0 \n","1 0.0 0.0 \n","2 0.0 0.0 \n","3 0.0 0.0 \n","4 0.0 0.0 \n","... ... ... \n","6912865 0.0 1.0 \n","6912866 0.0 1.0 \n","6912867 0.0 1.0 \n","6912868 0.0 1.0 \n","6912869 0.0 1.0 \n","\n","[6912870 rows x 16 columns]\n","Length of merged_df: 6912870\n","Total Length: 21635099\n"]},{"output_type":"stream","name":"stderr","text":["/tmp/ipykernel_17578/833223507.py:14: SettingWithCopyWarning: \n","A value is trying to be set on a copy of a slice from a DataFrame\n","\n","See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy\n"," correction_df.drop_duplicates(inplace=True)\n"]},{"output_type":"stream","name":"stdout","text":["Correction_df length: 1325096\n","Total Corrections: 4329081\n","3 Champawat\n","merged_df>>> .geo rh98_2020 \\\n","0 {\"geodesic\":false,\"type\":\"Point\",\"coordinates\"... 0.0 \n","1 {\"geodesic\":false,\"type\":\"Point\",\"coordinates\"... 1.0 \n","2 {\"geodesic\":false,\"type\":\"Point\",\"coordinates\"... 1.0 \n","3 {\"geodesic\":false,\"type\":\"Point\",\"coordinates\"... 0.0 \n","4 {\"geodesic\":false,\"type\":\"Point\",\"coordinates\"... 1.0 \n","... ... ... \n","2951210 {\"geodesic\":false,\"type\":\"Point\",\"coordinates\"... 0.0 \n","2951211 {\"geodesic\":false,\"type\":\"Point\",\"coordinates\"... 0.0 \n","2951212 {\"geodesic\":false,\"type\":\"Point\",\"coordinates\"... 0.0 \n","2951213 {\"geodesic\":false,\"type\":\"Point\",\"coordinates\"... 0.0 \n","2951214 {\"geodesic\":false,\"type\":\"Point\",\"coordinates\"... 1.0 \n","\n"," rh75_2020 rh50_2020 rh98_2021 rh75_2021 rh50_2021 rh98_2022 \\\n","0 1.0 1.0 0.0 0.0 1.0 0.0 \n","1 1.0 1.0 0.0 0.0 0.0 0.0 \n","2 1.0 1.0 1.0 0.0 0.0 0.0 \n","3 0.0 1.0 0.0 1.0 1.0 0.0 \n","4 1.0 1.0 0.0 0.0 0.0 0.0 \n","... ... ... ... ... ... ... \n","2951210 1.0 0.0 1.0 1.0 1.0 1.0 \n","2951211 0.0 0.0 1.0 1.0 1.0 1.0 \n","2951212 0.0 0.0 1.0 1.0 1.0 1.0 \n","2951213 0.0 0.0 1.0 1.0 1.0 1.0 \n","2951214 0.0 0.0 1.0 1.0 1.0 1.0 \n","\n"," rh75_2022 rh50_2022 rh98_2023 rh75_2023 rh50_2023 rh98_2024 \\\n","0 1.0 0.0 1.0 1.0 1.0 0.0 \n","1 1.0 1.0 1.0 1.0 1.0 1.0 \n","2 1.0 1.0 1.0 1.0 1.0 1.0 \n","3 1.0 1.0 1.0 1.0 1.0 0.0 \n","4 0.0 1.0 1.0 1.0 1.0 0.0 \n","... ... ... ... ... ... ... \n","2951210 1.0 1.0 1.0 1.0 1.0 0.0 \n","2951211 1.0 1.0 1.0 1.0 1.0 1.0 \n","2951212 1.0 1.0 1.0 1.0 1.0 1.0 \n","2951213 1.0 1.0 1.0 1.0 1.0 1.0 \n","2951214 1.0 1.0 1.0 1.0 1.0 1.0 \n","\n"," rh75_2024 rh50_2024 \n","0 1.0 1.0 \n","1 1.0 1.0 \n","2 0.0 0.0 \n","3 1.0 1.0 \n","4 0.0 1.0 \n","... ... ... \n","2951210 0.0 0.0 \n","2951211 0.0 0.0 \n","2951212 1.0 0.0 \n","2951213 1.0 1.0 \n","2951214 1.0 1.0 \n","\n","[2951215 rows x 16 columns]\n","Length of merged_df: 2951215\n","Total Length: 24586314\n"]},{"output_type":"stream","name":"stderr","text":["/tmp/ipykernel_17578/833223507.py:14: SettingWithCopyWarning: \n","A value is trying to be set on a copy of a slice from a DataFrame\n","\n","See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy\n"," correction_df.drop_duplicates(inplace=True)\n"]},{"output_type":"stream","name":"stdout","text":["Correction_df length: 762567\n","Total Corrections: 5091648\n"]}],"source":["# Function to convert string representation of list to an actual list\n","def convert_to_list(string):\n"," return ast.literal_eval(string)\n","\n","\n","for agroclimatic_zone in acz_list:\n"," print(agroclimatic_zone)\n","\n"," df = pd.read_csv('drive/MyDrive/TreeHealth/district_to_agroclimaticZone_mapping.csv')\n"," df['IntersectingZones'] = df['IntersectingZones'].apply(convert_to_list)\n"," district_mapping_df = df[df['AgroclimaticZone'] == agroclimatic_zone][['District', 'IntersectingZones']]\n"," dist_list = []\n"," for ind in district_mapping_df.index:\n"," district = district_mapping_df.loc[ind, 'District']\n"," zones = district_mapping_df['IntersectingZones'][ind]\n"," dist_list.append(district)\n","\n"," dist_list = ['Dehradun', 'Garhwal', 'Nainital', 'Champawat']\n"," print(f'len(dist_list): {len(dist_list)}')\n"," print(f'dist_list=: {dist_list}')\n","\n"," path = f'drive/MyDrive/TreeHealth/{agroclimatic_zone}/'\n","\n"," total_corrections = 0\n"," total_length = 0\n","\n"," for i in range(len(dist_list)):\n","\n"," if i == 4 or i==6:\n"," continue\n","\n"," print(i, dist_list[i])\n"," file_4 = path + dist_list[i] + f\"/{year_4}/result_chm.csv\"\n"," file_3 = path + dist_list[i] + f\"/{year_3}/result_chm.csv\"\n"," file_2 = path + dist_list[i] + f\"/{year_2}/result_chm.csv\"\n"," file_1 = path + dist_list[i] + f\"/{year_1}/result_chm.csv\"\n"," file_0 = path + dist_list[i] + f\"/{year}/result_chm.csv\"\n","\n"," try:\n"," df_4 = pd.read_csv(file_4)\n"," df_4.rename(columns={'rh98_class': f'rh98_{year_4}', 'rh75_class': f'rh75_{year_4}', 'rh50_class': f'rh50_{year_4}'}, inplace=True)\n","\n"," except Exception as e:\n"," print(e)\n"," df_4 = pd.DataFrame(columns=['.geo', f'rh98_{year_4}', f'rh75_{year_4}', f'rh50_{year_4}', f'ch_{year_4}'])\n","\n"," try:\n"," df_3 = pd.read_csv(file_3)\n"," df_3.rename(columns={'rh98_class': f'rh98_{year_3}', 'rh75_class': f'rh75_{year_3}', 'rh50_class': f'rh50_{year_3}'}, inplace=True)\n","\n"," except Exception as e:\n"," print(e)\n"," df_3 = pd.DataFrame(columns=['.geo', f'rh98_{year_3}', f'rh75_{year_3}', f'rh50_{year_3}', f'ch_{year_3}'])\n","\n"," merged_df = pd.merge(df_4, df_3, on='.geo', how='outer')\n"," del(df_4)\n"," del(df_3)\n","\n"," try:\n"," df_2 = pd.read_csv(file_2)\n"," df_2.rename(columns={'rh98_class': f'rh98_{year_2}', 'rh75_class': f'rh75_{year_2}', 'rh50_class': f'rh50_{year_2}'}, inplace=True)\n","\n"," except Exception as e:\n"," print(e)\n"," df_2 = pd.DataFrame(columns=['.geo', f'rh98_{year_2}', f'rh75_{year_2}', f'rh50_{year_2}', f'ch_{year_2}'])\n","\n"," merged_df = pd.merge(merged_df, df_2, on='.geo', how='outer')\n"," del(df_2)\n","\n"," try:\n"," df_1 = pd.read_csv(file_1)\n"," df_1.rename(columns={'rh98_class': f'rh98_{year_1}', 'rh75_class': f'rh75_{year_1}', 'rh50_class': f'rh50_{year_1}'}, inplace=True)\n"," except Exception as e:\n"," print(e)\n"," df_1 = pd.DataFrame(columns=['.geo', f'rh98_{year_1}', f'rh75_{year_1}', f'rh50_{year_1}', f'ch_{year_1}'])\n","\n"," merged_df = pd.merge(merged_df, df_1, on='.geo', how='outer')\n"," del(df_1)\n","\n"," try:\n"," df_0 = pd.read_csv(file_0)\n"," df_0.rename(columns={'rh98_class': f'rh98_{year}', 'rh75_class': f'rh75_{year}', 'rh50_class': f'rh50_{year}'}, inplace=True)\n","\n"," except Exception as e:\n"," print(e)\n"," df_0 = pd.DataFrame(columns=['.geo', f'rh98_{year}', f'rh75_{year}', f'rh50_{year}', f'ch_{year}'])\n","\n"," merged_df = pd.merge(merged_df, df_0, on='.geo', how='outer')\n"," del(df_0)\n"," print(\"merged_df>>>\",merged_df)\n","\n"," try:\n"," merged_df = merged_df[['.geo', f'rh98_{year_4}', f'rh98_{year_3}', f'rh98_{year_2}', f'rh98_{year_1}', f'rh98_{year}',\n"," f'rh75_{year_4}', f'rh75_{year_3}', f'rh75_{year_2}', f'rh75_{year_1}', f'rh75_{year}', f'rh50_{year_4}',\n"," f'rh50_{year_3}', f'rh50_{year_2}', f'rh50_{year_1}', f'rh50_{year}']]\n"," except Exception as e:\n"," print(e)\n","\n"," total_length += len(merged_df)\n"," print(\"Length of merged_df:\", len(merged_df))\n"," print(\"Total Length:\", total_length)\n"," if year == '2019':\n"," correction_df = ch_corrections_2017(merged_df) # Uncomment when correcting 2017\n"," else:\n"," correction_df = ch_corrections(merged_df) # Comment when correcting 2017\n","\n"," del(merged_df)\n","\n"," total_corrections += len(correction_df)\n"," print(f'Correction_df length: {len(correction_df)}')\n"," print(f'Total Corrections: {total_corrections}')\n"," if len(correction_df) > 0:\n","\n"," choices = [0, 0, 1, 2, 1, 2]\n","\n"," conditions = [\n"," (correction_df[f'rh50_{year_4}'] == 0) & (correction_df[f'rh75_{year_4}'] == 0) & (correction_df[f'rh98_{year_4}'] == 0),\n"," (correction_df[f'rh50_{year_4}'] == 0) & (correction_df[f'rh75_{year_4}'] == 0) & (correction_df[f'rh98_{year_4}'] == 1),\n"," (correction_df[f'rh50_{year_4}'] == 0) & (correction_df[f'rh75_{year_4}'] == 1) & (correction_df[f'rh98_{year_4}'] == 0),\n"," (correction_df[f'rh50_{year_4}'] == 0) & (correction_df[f'rh75_{year_4}'] == 1) & (correction_df[f'rh98_{year_4}'] == 1),\n"," (correction_df[f'rh50_{year_4}'] == 1) & (correction_df[f'rh75_{year_4}'] == 0) & (correction_df[f'rh98_{year_4}'] == 0),\n"," (correction_df[f'rh50_{year_4}'] == 1) & (correction_df[f'rh75_{year_4}'] == 0) & (correction_df[f'rh98_{year_4}'] == 1)\n"," ]\n"," correction_df[f'ch_{year_4}'] = np.select(conditions, choices, default=3)\n","\n","\n"," conditions = [\n"," (correction_df[f'rh50_{year_3}'] == 0) & (correction_df[f'rh75_{year_3}'] == 0) & (correction_df[f'rh98_{year_3}'] == 0),\n"," (correction_df[f'rh50_{year_3}'] == 0) & (correction_df[f'rh75_{year_3}'] == 0) & (correction_df[f'rh98_{year_3}'] == 1),\n"," (correction_df[f'rh50_{year_3}'] == 0) & (correction_df[f'rh75_{year_3}'] == 1) & (correction_df[f'rh98_{year_3}'] == 0),\n"," (correction_df[f'rh50_{year_3}'] == 0) & (correction_df[f'rh75_{year_3}'] == 1) & (correction_df[f'rh98_{year_3}'] == 1),\n"," (correction_df[f'rh50_{year_3}'] == 1) & (correction_df[f'rh75_{year_3}'] == 0) & (correction_df[f'rh98_{year_3}'] == 0),\n"," (correction_df[f'rh50_{year_3}'] == 1) & (correction_df[f'rh75_{year_3}'] == 0) & (correction_df[f'rh98_{year_3}'] == 1)\n"," ]\n"," correction_df[f'ch_{year_3}'] = np.select(conditions, choices, default=3)\n","\n","\n"," conditions = [\n"," (correction_df[f'rh50_{year_2}'] == 0) & (correction_df[f'rh75_{year_2}'] == 0) & (correction_df[f'rh98_{year_2}'] == 0),\n"," (correction_df[f'rh50_{year_2}'] == 0) & (correction_df[f'rh75_{year_2}'] == 0) & (correction_df[f'rh98_{year_2}'] == 1),\n"," (correction_df[f'rh50_{year_2}'] == 0) & (correction_df[f'rh75_{year_2}'] == 1) & (correction_df[f'rh98_{year_2}'] == 0),\n"," (correction_df[f'rh50_{year_2}'] == 0) & (correction_df[f'rh75_{year_2}'] == 1) & (correction_df[f'rh98_{year_2}'] == 1),\n"," (correction_df[f'rh50_{year_2}'] == 1) & (correction_df[f'rh75_{year_2}'] == 0) & (correction_df[f'rh98_{year_2}'] == 0),\n"," (correction_df[f'rh50_{year_2}'] == 1) & (correction_df[f'rh75_{year_2}'] == 0) & (correction_df[f'rh98_{year_2}'] == 1)\n"," ]\n"," correction_df[f'ch_{year_2}'] = np.select(conditions, choices, default=3)\n","\n","\n"," conditions = [\n"," (correction_df[f'rh50_{year_1}'] == 0) & (correction_df[f'rh75_{year_1}'] == 0) & (correction_df[f'rh98_{year_1}'] == 0),\n"," (correction_df[f'rh50_{year_1}'] == 0) & (correction_df[f'rh75_{year_1}'] == 0) & (correction_df[f'rh98_{year_1}'] == 1),\n"," (correction_df[f'rh50_{year_1}'] == 0) & (correction_df[f'rh75_{year_1}'] == 1) & (correction_df[f'rh98_{year_1}'] == 0),\n"," (correction_df[f'rh50_{year_1}'] == 0) & (correction_df[f'rh75_{year_1}'] == 1) & (correction_df[f'rh98_{year_1}'] == 1),\n"," (correction_df[f'rh50_{year_1}'] == 1) & (correction_df[f'rh75_{year_1}'] == 0) & (correction_df[f'rh98_{year_1}'] == 0),\n"," (correction_df[f'rh50_{year_1}'] == 1) & (correction_df[f'rh75_{year_1}'] == 0) & (correction_df[f'rh98_{year_1}'] == 1)\n"," ]\n"," correction_df[f'ch_{year_1}'] = np.select(conditions, choices, default=3)\n","\n","\n"," conditions = [\n"," (correction_df[f'rh50_{year}'] == 0) & (correction_df[f'rh75_{year}'] == 0) & (correction_df[f'rh98_{year}'] == 0),\n"," (correction_df[f'rh50_{year}'] == 0) & (correction_df[f'rh75_{year}'] == 0) & (correction_df[f'rh98_{year}'] == 1),\n"," (correction_df[f'rh50_{year}'] == 0) & (correction_df[f'rh75_{year}'] == 1) & (correction_df[f'rh98_{year}'] == 0),\n"," (correction_df[f'rh50_{year}'] == 0) & (correction_df[f'rh75_{year}'] == 1) & (correction_df[f'rh98_{year}'] == 1),\n"," (correction_df[f'rh50_{year}'] == 1) & (correction_df[f'rh75_{year}'] == 0) & (correction_df[f'rh98_{year}'] == 0),\n"," (correction_df[f'rh50_{year}'] == 1) & (correction_df[f'rh75_{year}'] == 0) & (correction_df[f'rh98_{year}'] == 1)\n"," ]\n"," correction_df[f'ch_{year}'] = np.select(conditions, choices, default=3)\n","\n"," cols = correction_df.columns\n"," for j in range(1, len(cols)):\n"," correction_df[cols[j]] = correction_df[cols[j]].astype('Int64')\n"," # if year != last_year: # TODO remove this\n"," correction_year_2 = correction_df[['.geo', f'rh50_{year_2}', f'rh75_{year_2}', f'rh98_{year_2}', f'ch_{year_2}']]\n"," correction_year_2.to_csv(f'{path}{dist_list[i]}/{year_2}/result_chm_corrections.csv', index=False)\n"," if year != '2019':\n"," correction_year_1 = correction_df[['.geo', f'rh50_{year_1}', f'rh75_{year_1}', f'rh98_{year_1}', f'ch_{year_1}']] # Comment when correcting 2017\n"," correction_year_1.to_csv(f'{path}{dist_list[i]}/{year_1}/result_chm_corrections.csv', index=False) # Comment when correcting 2017\n","\n"," del(correction_df)\n"]},{"cell_type":"markdown","metadata":{"id":"ZwMptI_mVQ4a"},"source":["CH - correction Chunking (Run this only for those locations which are causing RAM issue)"]},{"cell_type":"code","execution_count":null,"metadata":{"colab":{"base_uri":"https://localhost:8080/"},"id":"eNlS8jKW3tom","executionInfo":{"status":"ok","timestamp":1774375877955,"user_tz":-330,"elapsed":6162811,"user":{"displayName":"CoRE Stack","userId":"15795073488475210764"}},"outputId":"ca3318b0-6fe1-42e9-c59f-03e707e8339c"},"outputs":[{"output_type":"stream","name":"stdout","text":["2024\n","Eastern Plateau & Hills Region\n","len(dist_list): 1\n","dist_list: ['Garhchiroli']\n","0 District Garhchiroli\n","File 4\n","File 3\n","File 2\n","File 1\n","File 0\n","Total unique .geo keys (union): 20257293\n","Processing 6 merge-chunks of size up to 4000000...\n","Processing SQL chunk 1/6 with 4000000 rows\n","Length of merged_df (this chunk): 4000000\n","Total Length so far: 4000000\n"]},{"output_type":"stream","name":"stderr","text":["/tmp/ipykernel_6773/833223507.py:14: SettingWithCopyWarning: \n","A value is trying to be set on a copy of a slice from a DataFrame\n","\n","See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy\n"," correction_df.drop_duplicates(inplace=True)\n"]},{"output_type":"stream","name":"stdout","text":["Correction_df length (chunk): 251390\n","Total Corrections so far: 251390\n","Processing SQL chunk 2/6 with 4000000 rows\n","Length of merged_df (this chunk): 4000000\n","Total Length so far: 8000000\n"]},{"output_type":"stream","name":"stderr","text":["/tmp/ipykernel_6773/833223507.py:14: SettingWithCopyWarning: \n","A value is trying to be set on a copy of a slice from a DataFrame\n","\n","See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy\n"," correction_df.drop_duplicates(inplace=True)\n"]},{"output_type":"stream","name":"stdout","text":["Correction_df length (chunk): 365821\n","Total Corrections so far: 617211\n","Processing SQL chunk 3/6 with 4000000 rows\n","Length of merged_df (this chunk): 4000000\n","Total Length so far: 12000000\n"]},{"output_type":"stream","name":"stderr","text":["/tmp/ipykernel_6773/833223507.py:14: SettingWithCopyWarning: \n","A value is trying to be set on a copy of a slice from a DataFrame\n","\n","See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy\n"," correction_df.drop_duplicates(inplace=True)\n"]},{"output_type":"stream","name":"stdout","text":["Correction_df length (chunk): 530155\n","Total Corrections so far: 1147366\n","Processing SQL chunk 4/6 with 4000000 rows\n","Length of merged_df (this chunk): 4000000\n","Total Length so far: 16000000\n"]},{"output_type":"stream","name":"stderr","text":["/tmp/ipykernel_6773/833223507.py:14: SettingWithCopyWarning: \n","A value is trying to be set on a copy of a slice from a DataFrame\n","\n","See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy\n"," correction_df.drop_duplicates(inplace=True)\n"]},{"output_type":"stream","name":"stdout","text":["Correction_df length (chunk): 626236\n","Total Corrections so far: 1773602\n","Processing SQL chunk 5/6 with 4000000 rows\n","Length of merged_df (this chunk): 4000000\n","Total Length so far: 20000000\n"]},{"output_type":"stream","name":"stderr","text":["/tmp/ipykernel_6773/833223507.py:14: SettingWithCopyWarning: \n","A value is trying to be set on a copy of a slice from a DataFrame\n","\n","See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy\n"," correction_df.drop_duplicates(inplace=True)\n"]},{"output_type":"stream","name":"stdout","text":["Correction_df length (chunk): 824080\n","Total Corrections so far: 2597682\n","Processing SQL chunk 6/6 with 257293 rows\n","Length of merged_df (this chunk): 257293\n","Total Length so far: 20257293\n"]},{"output_type":"stream","name":"stderr","text":["/tmp/ipykernel_6773/833223507.py:14: SettingWithCopyWarning: \n","A value is trying to be set on a copy of a slice from a DataFrame\n","\n","See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy\n"," correction_df.drop_duplicates(inplace=True)\n"]},{"output_type":"stream","name":"stdout","text":["Correction_df length (chunk): 54985\n","Total Corrections so far: 2652667\n"]}],"source":["# Add imports at top of your file (if not already imported)\n","import sqlite3\n","import os\n","import math\n","from pathlib import Path\n","\n","CHUNKSIZE = 4000_000\n","print(year)\n","for agroclimatic_zone in acz_list:\n"," print(agroclimatic_zone)\n","\n"," df = pd.read_csv('drive/MyDrive/TreeHealth/district_to_agroclimaticZone_mapping.csv')\n"," df['IntersectingZones'] = df['IntersectingZones'].apply(ast.literal_eval)\n"," district_mapping_df = df[df['AgroclimaticZone'] == agroclimatic_zone][['District', 'IntersectingZones']]\n","\n"," dist_list = district_mapping_df['District'].tolist()\n"," dist_list = ['Garhchiroli']\n"," print(f'len(dist_list): {len(dist_list)}')\n"," print(f'dist_list: {dist_list}')\n","\n"," path = f'drive/MyDrive/TreeHealth/{agroclimatic_zone}/'\n"," total_corrections = 0\n"," total_length = 0\n","\n","\n"," for i in range(len(dist_list)):\n"," print(i, \"District\",dist_list[i])\n"," file_4 = f\"{path}{dist_list[i]}/{year_4}/result_chm.csv\"\n"," file_3 = f\"{path}{dist_list[i]}/{year_3}/result_chm.csv\"\n"," file_2 = f\"{path}{dist_list[i]}/{year_2}/result_chm.csv\"\n"," file_1 = f\"{path}{dist_list[i]}/{year_1}/result_chm.csv\"\n"," file_0 = f\"{path}{dist_list[i]}/{year}/result_chm.csv\"\n","\n"," # --- Begin drop-in chunked-on-disk merge logic ---\n"," # Create temp sqlite DB per district to avoid collisions and keep things local to each district\n"," db_path = f'{path}{dist_list[i]}/temp_merge.db'\n"," # ensure directory exists\n"," Path(os.path.dirname(db_path)).mkdir(parents=True, exist_ok=True)\n"," # remove previous DB if exists (so headers won't collide when appending)\n"," if os.path.exists(db_path):\n"," try:\n"," os.remove(db_path)\n"," except Exception:\n"," pass\n","\n"," conn = sqlite3.connect(db_path)\n"," # Use pragmas to speed inserts (safe for temp file)\n"," conn.execute('PRAGMA synchronous=OFF;')\n"," conn.execute('PRAGMA journal_mode=MEMORY;')\n"," conn.commit()\n","\n"," # helper to stream a CSV into an sqlite table; normalizes column names: .geo -> geo, rh*_class -> rh*_\n"," def stream_csv_to_table(csv_file, table_name, year_tag):\n"," # if file doesn't exist, create empty table with expected schema (so joins work)\n"," if not os.path.exists(csv_file) or os.path.getsize(csv_file) <= 1:\n"," # create empty table with schema\n"," conn.execute(f'''\n"," CREATE TABLE IF NOT EXISTS {table_name} (\n"," geo TEXT PRIMARY KEY,\n"," rh98_{year_tag} INTEGER,\n"," rh75_{year_tag} INTEGER,\n"," rh50_{year_tag} INTEGER\n"," )\n"," ''')\n"," conn.commit()\n"," return\n","\n"," cols = ['.geo', 'rh98_class', 'rh75_class', 'rh50_class']\n"," try:\n"," reader = pd.read_csv(csv_file, usecols=cols, chunksize=CHUNKSIZE, iterator=True)\n"," except Exception as e:\n"," # if reading with those columns fails, fall back to reading only .geo\n"," reader = pd.read_csv(csv_file, chunksize=CHUNKSIZE, iterator=True)\n","\n"," any_rows = False\n","\n"," for chunk in reader:\n"," any_rows = True\n"," # normalize column names and keep only what we need\n"," if '.geo' in chunk.columns:\n"," chunk = chunk.rename(columns={\n"," '.geo': 'geo',\n"," 'rh98_class': f'rh98_{year_tag}',\n"," 'rh75_class': f'rh75_{year_tag}',\n"," 'rh50_class': f'rh50_{year_tag}'\n"," })\n"," # ensure columns exist even if file lacked them\n"," for c in [f'rh98_{year_tag}', f'rh75_{year_tag}', f'rh50_{year_tag}']:\n"," if c not in chunk.columns:\n"," chunk[c] = pd.NA\n","\n"," chunk[['geo', f'rh98_{year_tag}', f'rh75_{year_tag}', f'rh50_{year_tag}']].to_sql(\n"," table_name, conn, if_exists='append', index=False\n"," )\n","\n"," # If the file had headers but no data rows,\n"," # make sure the table still exists with the right schema\n"," if not any_rows:\n"," conn.execute(f'''\n"," CREATE TABLE IF NOT EXISTS {table_name} (\n"," geo TEXT PRIMARY KEY,\n"," rh98_{year_tag} INTEGER,\n"," rh75_{year_tag} INTEGER,\n"," rh50_{year_tag} INTEGER\n"," )\n"," ''')\n"," conn.commit()\n","\n"," # Stream each file into its own sqlite table\n"," print(\"File 4\")\n"," stream_csv_to_table(file_4, 't4', year_4)\n"," print(\"File 3\")\n"," stream_csv_to_table(file_3, 't3', year_3)\n"," print(\"File 2\")\n"," stream_csv_to_table(file_2, 't2', year_2)\n"," print(\"File 1\")\n"," stream_csv_to_table(file_1, 't1', year_1)\n"," print(\"File 0\")\n"," stream_csv_to_table(file_0, 't0', year)\n","\n"," # Build union of all geo keys (on-disk), then count total rows\n"," union_sql = '''\n"," SELECT geo FROM t4\n"," UNION\n"," SELECT geo FROM t3\n"," UNION\n"," SELECT geo FROM t2\n"," UNION\n"," SELECT geo FROM t1\n"," UNION\n"," SELECT geo FROM t0\n"," '''\n"," count_sql = f\"SELECT COUNT(*) FROM ({union_sql}) AS u\"\n"," cursor = conn.execute(count_sql)\n"," total_rows = cursor.fetchone()[0]\n"," print(\"Total unique .geo keys (union):\", total_rows)\n","\n"," # process in CHUNKSIZE slices from the union\n"," num_chunks = math.ceil(total_rows / CHUNKSIZE)\n"," print(f\"Processing {num_chunks} merge-chunks of size up to {CHUNKSIZE}...\")\n","\n"," # build a parameterized select that left-joins each year's table to the union subquery\n"," chunk_select_template = f'''\n"," SELECT u.geo AS \".geo\",\n"," t4.rh98_{year_4} as rh98_{year_4}, t4.rh75_{year_4} as rh75_{year_4}, t4.rh50_{year_4} as rh50_{year_4},\n"," t3.rh98_{year_3} as rh98_{year_3}, t3.rh75_{year_3} as rh75_{year_3}, t3.rh50_{year_3} as rh50_{year_3},\n"," t2.rh98_{year_2} as rh98_{year_2}, t2.rh75_{year_2} as rh75_{year_2}, t2.rh50_{year_2} as rh50_{year_2},\n"," t1.rh98_{year_1} as rh98_{year_1}, t1.rh75_{year_1} as rh75_{year_1}, t1.rh50_{year_1} as rh50_{year_1},\n"," t0.rh98_{year} as rh98_{year}, t0.rh75_{year} as rh75_{year}, t0.rh50_{year} as rh50_{year}\n"," FROM (\n"," {union_sql}\n"," ORDER BY geo\n"," LIMIT ? OFFSET ?\n"," ) u\n"," LEFT JOIN t4 ON u.geo = t4.geo\n"," LEFT JOIN t3 ON u.geo = t3.geo\n"," LEFT JOIN t2 ON u.geo = t2.geo\n"," LEFT JOIN t1 ON u.geo = t1.geo\n"," LEFT JOIN t0 ON u.geo = t0.geo\n"," '''\n","\n"," # remove any existing output correction files for this district/year so we can append anew\n"," out_file_y2 = f'{path}{dist_list[i]}/{year_2}/result_chm_corrections.csv'\n"," out_file_y1 = f'{path}{dist_list[i]}/{year_1}/result_chm_corrections.csv'\n"," # We'll append chunk outputs; delete existing so headers are added correctly (mimic original behaviour)\n"," if os.path.exists(out_file_y2):\n"," os.remove(out_file_y2)\n"," if os.path.exists(out_file_y1) and year != '2019':\n"," os.remove(out_file_y1)\n","\n"," for chunk_idx in range(num_chunks):\n"," offset = chunk_idx * CHUNKSIZE\n"," params = (CHUNKSIZE, offset)\n"," # read chunk into pandas\n"," df_chunk = pd.read_sql_query(chunk_select_template, conn, params=params)\n","\n"," # rename the 'year' columns to include the actual year variable names used in the template above\n"," # (they already are named properly except the generic 'rh98_{year}' placeholders which the SQL aliased as rh98_{year})\n"," # ensure types and presence of columns, then proceed exactly as your old code expects (select columns etc.)\n"," print(f\"Processing SQL chunk {chunk_idx+1}/{num_chunks} with {len(df_chunk)} rows\")\n"," total_length += len(df_chunk)\n"," print(\"Length of merged_df (this chunk):\", len(df_chunk))\n"," print(\"Total Length so far:\", total_length)\n","\n"," # Recreate expected column selection (same as original)\n"," try:\n"," merged_df = df_chunk[['.geo',\n"," f'rh98_{year_4}', f'rh98_{year_3}', f'rh98_{year_2}', f'rh98_{year_1}', f'rh98_{year}',\n"," f'rh75_{year_4}', f'rh75_{year_3}', f'rh75_{year_2}', f'rh75_{year_1}', f'rh75_{year}',\n"," f'rh50_{year_4}', f'rh50_{year_3}', f'rh50_{year_2}', f'rh50_{year_1}', f'rh50_{year}']]\n"," except Exception as e:\n"," print(\"Column selection error on chunk:\", e)\n"," # Continue with whatever columns are present\n"," merged_df = df_chunk.copy()\n","\n"," # run correction on this chunk exactly like before\n"," if year == '2019':\n"," correction_df = ch_corrections_2017(merged_df)\n"," else:\n"," correction_df = ch_corrections(merged_df)\n","\n"," del(merged_df)\n","\n"," total_corrections += len(correction_df)\n"," print(f'Correction_df length (chunk): {len(correction_df)}')\n"," print(f'Total Corrections so far: {total_corrections}')\n","\n"," if len(correction_df) > 0:\n"," # same CH assignment logic as before - keep exactly the same code\n"," choices = [0, 0, 1, 2, 1, 2]\n","\n"," conditions = [\n"," (correction_df[f'rh50_{year_4}'] == 0) & (correction_df[f'rh75_{year_4}'] == 0) & (correction_df[f'rh98_{year_4}'] == 0),\n"," (correction_df[f'rh50_{year_4}'] == 0) & (correction_df[f'rh75_{year_4}'] == 0) & (correction_df[f'rh98_{year_4}'] == 1),\n"," (correction_df[f'rh50_{year_4}'] == 0) & (correction_df[f'rh75_{year_4}'] == 1) & (correction_df[f'rh98_{year_4}'] == 0),\n"," (correction_df[f'rh50_{year_4}'] == 0) & (correction_df[f'rh75_{year_4}'] == 1) & (correction_df[f'rh98_{year_4}'] == 1),\n"," (correction_df[f'rh50_{year_4}'] == 1) & (correction_df[f'rh75_{year_4}'] == 0) & (correction_df[f'rh98_{year_4}'] == 0),\n"," (correction_df[f'rh50_{year_4}'] == 1) & (correction_df[f'rh75_{year_4}'] == 0) & (correction_df[f'rh98_{year_4}'] == 1)\n"," ]\n"," correction_df[f'ch_{year_4}'] = np.select(conditions, choices, default=3)\n","\n"," conditions = [\n"," (correction_df[f'rh50_{year_3}'] == 0) & (correction_df[f'rh75_{year_3}'] == 0) & (correction_df[f'rh98_{year_3}'] == 0),\n"," (correction_df[f'rh50_{year_3}'] == 0) & (correction_df[f'rh75_{year_3}'] == 0) & (correction_df[f'rh98_{year_3}'] == 1),\n"," (correction_df[f'rh50_{year_3}'] == 0) & (correction_df[f'rh75_{year_3}'] == 1) & (correction_df[f'rh98_{year_3}'] == 0),\n"," (correction_df[f'rh50_{year_3}'] == 0) & (correction_df[f'rh75_{year_3}'] == 1) & (correction_df[f'rh98_{year_3}'] == 1),\n"," (correction_df[f'rh50_{year_3}'] == 1) & (correction_df[f'rh75_{year_3}'] == 0) & (correction_df[f'rh98_{year_3}'] == 0),\n"," (correction_df[f'rh50_{year_3}'] == 1) & (correction_df[f'rh75_{year_3}'] == 0) & (correction_df[f'rh98_{year_3}'] == 1)\n"," ]\n"," correction_df[f'ch_{year_3}'] = np.select(conditions, choices, default=3)\n","\n"," conditions = [\n"," (correction_df[f'rh50_{year_2}'] == 0) & (correction_df[f'rh75_{year_2}'] == 0) & (correction_df[f'rh98_{year_2}'] == 0),\n"," (correction_df[f'rh50_{year_2}'] == 0) & (correction_df[f'rh75_{year_2}'] == 0) & (correction_df[f'rh98_{year_2}'] == 1),\n"," (correction_df[f'rh50_{year_2}'] == 0) & (correction_df[f'rh75_{year_2}'] == 1) & (correction_df[f'rh98_{year_2}'] == 0),\n"," (correction_df[f'rh50_{year_2}'] == 0) & (correction_df[f'rh75_{year_2}'] == 1) & (correction_df[f'rh98_{year_2}'] == 1),\n"," (correction_df[f'rh50_{year_2}'] == 1) & (correction_df[f'rh75_{year_2}'] == 0) & (correction_df[f'rh98_{year_2}'] == 0),\n"," (correction_df[f'rh50_{year_2}'] == 1) & (correction_df[f'rh75_{year_2}'] == 0) & (correction_df[f'rh98_{year_2}'] == 1)\n"," ]\n"," correction_df[f'ch_{year_2}'] = np.select(conditions, choices, default=3)\n","\n"," conditions = [\n"," (correction_df[f'rh50_{year_1}'] == 0) & (correction_df[f'rh75_{year_1}'] == 0) & (correction_df[f'rh98_{year_1}'] == 0),\n"," (correction_df[f'rh50_{year_1}'] == 0) & (correction_df[f'rh75_{year_1}'] == 0) & (correction_df[f'rh98_{year_1}'] == 1),\n"," (correction_df[f'rh50_{year_1}'] == 0) & (correction_df[f'rh75_{year_1}'] == 1) & (correction_df[f'rh98_{year_1}'] == 0),\n"," (correction_df[f'rh50_{year_1}'] == 0) & (correction_df[f'rh75_{year_1}'] == 1) & (correction_df[f'rh98_{year_1}'] == 1),\n"," (correction_df[f'rh50_{year_1}'] == 1) & (correction_df[f'rh75_{year_1}'] == 0) & (correction_df[f'rh98_{year_1}'] == 0),\n"," (correction_df[f'rh50_{year_1}'] == 1) & (correction_df[f'rh75_{year_1}'] == 0) & (correction_df[f'rh98_{year_1}'] == 1)\n"," ]\n"," correction_df[f'ch_{year_1}'] = np.select(conditions, choices, default=3)\n","\n"," conditions = [\n"," (correction_df[f'rh50_{year}'] == 0) & (correction_df[f'rh75_{year}'] == 0) & (correction_df[f'rh98_{year}'] == 0),\n"," (correction_df[f'rh50_{year}'] == 0) & (correction_df[f'rh75_{year}'] == 0) & (correction_df[f'rh98_{year}'] == 1),\n"," (correction_df[f'rh50_{year}'] == 0) & (correction_df[f'rh75_{year}'] == 1) & (correction_df[f'rh98_{year}'] == 0),\n"," (correction_df[f'rh50_{year}'] == 0) & (correction_df[f'rh75_{year}'] == 1) & (correction_df[f'rh98_{year}'] == 1),\n"," (correction_df[f'rh50_{year}'] == 1) & (correction_df[f'rh75_{year}'] == 0) & (correction_df[f'rh98_{year}'] == 0),\n"," (correction_df[f'rh50_{year}'] == 1) & (correction_df[f'rh75_{year}'] == 0) & (correction_df[f'rh98_{year}'] == 1)\n"," ]\n"," correction_df[f'ch_{year}'] = np.select(conditions, choices, default=3)\n","\n"," cols = correction_df.columns\n"," for j in range(1, len(cols)):\n"," correction_df[cols[j]] = correction_df[cols[j]].astype('Int64')\n","\n"," # Write outputs appending chunk results (same filenames as before)\n"," # if year != last_year:\n"," # correction_year_2 = correction_df[['.geo', f'rh50_{year_2}', f'rh75_{year_2}', f'rh98_{year_2}', f'ch_{year_2}']]\n"," correction_year_2 = correction_df[correction_df[f'ch_{year_2}'].notna()][['.geo', f'rh50_{year_2}', f'rh75_{year_2}', f'rh98_{year_2}', f'ch_{year_2}']]\n"," correction_year_2.to_csv(out_file_y2, mode='a', index=False, header=not os.path.exists(out_file_y2))\n","\n"," if year != '2019':\n"," # correction_year_1 = correction_df[['.geo', f'rh50_{year_1}', f'rh75_{year_1}', f'rh98_{year_1}', f'ch_{year_1}']]\n"," correction_year_1 = correction_df[correction_df[f'ch_{year_1}'].notna()][['.geo', f'rh50_{year_1}', f'rh75_{year_1}', f'rh98_{year_1}', f'ch_{year_1}']]\n"," correction_year_1.to_csv(out_file_y1, mode='a', index=False, header=not os.path.exists(out_file_y1))\n","\n"," del(correction_df)\n","\n"," # cleanup sqlite DB\n"," conn.close()\n"," try:\n"," os.remove(db_path)\n"," except Exception:\n"," pass\n","\n"," # --- End drop-in chunked-on-disk merge logic ---"]}],"metadata":{"colab":{"collapsed_sections":["OpBWXM1TMVAs"],"provenance":[{"file_id":"1mrIZUaD4wZbGsDhalhghUuHK-uYOTTew","timestamp":1758914446736}]},"kernelspec":{"display_name":"Python 3","name":"python3"},"language_info":{"name":"python"}},"nbformat":4,"nbformat_minor":0} \ No newline at end of file diff --git a/utilities/scripts/tree_health/colab_notebooks/uploadAssets.ipynb b/utilities/scripts/tree_health/colab_notebooks/uploadAssets.ipynb new file mode 100644 index 00000000..a7b0281d --- /dev/null +++ b/utilities/scripts/tree_health/colab_notebooks/uploadAssets.ipynb @@ -0,0 +1 @@ +{"cells":[{"cell_type":"code","execution_count":1,"metadata":{"executionInfo":{"elapsed":13357,"status":"ok","timestamp":1775284491086,"user":{"displayName":"CoRE Stack","userId":"15795073488475210764"},"user_tz":-330},"id":"yewisZzO_XBA"},"outputs":[],"source":["import ee\n","import pandas as pd\n","import json\n","from glob import glob\n","import geemap\n","import pyproj\n","from geopandas import geopandas as gpd\n","from shapely.geometry import Point\n","from copy import deepcopy\n","import numpy as np\n","import os\n","import ast\n","from google.cloud import storage"]},{"cell_type":"code","execution_count":null,"metadata":{"colab":{"base_uri":"https://localhost:8080/","height":17},"executionInfo":{"elapsed":22,"status":"ok","timestamp":1774410186498,"user":{"displayName":"CoRE Stack","userId":"15795073488475210764"},"user_tz":-330},"id":"iIy2d0N33YVM","outputId":"d30444eb-aa3c-47f4-a361-2793d2fe07a2"},"outputs":[{"output_type":"display_data","data":{"text/plain":[""],"text/html":["\n"," \n"," "]},"metadata":{}}],"source":["# from google.colab import drive\n","# drive.flush_and_unmount()"]},{"cell_type":"code","execution_count":2,"metadata":{"colab":{"base_uri":"https://localhost:8080/","height":35},"executionInfo":{"elapsed":50444,"status":"ok","timestamp":1775284560071,"user":{"displayName":"CoRE Stack","userId":"15795073488475210764"},"user_tz":-330},"id":"XOwz3mnBYzzf","outputId":"da646a7e-387e-4fd0-ea21-11a6859e69e4"},"outputs":[{"output_type":"display_data","data":{"text/plain":[""],"text/html":["\n"," \n"," "]},"metadata":{}},{"output_type":"stream","name":"stdout","text":["Mounted at /content/drive\n"]}],"source":["from google.colab import drive\n","drive.mount('/content/drive')"]},{"cell_type":"code","execution_count":3,"metadata":{"colab":{"base_uri":"https://localhost:8080/","height":17},"executionInfo":{"elapsed":44694,"status":"ok","timestamp":1775284604766,"user":{"displayName":"CoRE Stack","userId":"15795073488475210764"},"user_tz":-330},"id":"EmZdWlXYBv-I","outputId":"b6bfe92a-5c89-470a-ee5b-add2fbf5b974"},"outputs":[{"output_type":"display_data","data":{"text/plain":[""],"text/html":["\n"," \n"," "]},"metadata":{}}],"source":["ee.Authenticate()\n","ee_project = \"corestack1-dev-alpha\"\n","ee.Initialize(project=\"core-stack-dev-2\")#\"corestack1-dev-alpha\")"]},{"cell_type":"code","execution_count":4,"metadata":{"colab":{"base_uri":"https://localhost:8080/","height":17},"executionInfo":{"elapsed":1407,"status":"ok","timestamp":1775284606174,"user":{"displayName":"CoRE Stack","userId":"15795073488475210764"},"user_tz":-330},"id":"SnpGWiMmDvM3","outputId":"36fbf7ff-49be-4ed0-bd4c-ff348b3a593c"},"outputs":[{"output_type":"display_data","data":{"text/plain":[""],"text/html":["\n"," \n"," "]},"metadata":{}}],"source":["client = storage.Client()\n","bucket = client.get_bucket('core_stack')"]},{"cell_type":"code","execution_count":5,"metadata":{"colab":{"base_uri":"https://localhost:8080/","height":17},"executionInfo":{"elapsed":4,"status":"ok","timestamp":1775284606179,"user":{"displayName":"CoRE Stack","userId":"15795073488475210764"},"user_tz":-330},"id":"RT2Qqbs-Y4Qv","outputId":"b9727627-6f15-48e8-d405-a42d433c899e"},"outputs":[{"output_type":"display_data","data":{"text/plain":[""],"text/html":["\n"," \n"," "]},"metadata":{}}],"source":["best_month_dict = {'Eastern Plateau & Hills Region': 'cc_12',\n"," 'Middle Gangetic Plain Region': 'cc_10',\n"," 'Lower Gangetic Plain Region': 'cc_9',\n"," 'Western Himalayan Region': 'cc_8',\n"," 'Eastern Himalayan Region': 'cc_10',\n"," 'Upper Gangetic Plain Region': 'cc_9',\n"," 'Trans Gangetic Plain Region': 'cc_9',\n"," 'Central Plateau & Hills Region': 'cc_7',\n"," 'Western Plateau and Hills Region': 'cc_11',\n"," 'Southern Plateau and Hills Region': 'cc_8',\n"," 'East Coast Plains & Hills Region': 'cc_12'}"]},{"cell_type":"code","execution_count":6,"metadata":{"colab":{"base_uri":"https://localhost:8080/","height":17},"executionInfo":{"elapsed":8,"status":"ok","timestamp":1775284609820,"user":{"displayName":"CoRE Stack","userId":"15795073488475210764"},"user_tz":-330},"id":"qoDgejJ4Y7uk","outputId":"54668df5-bb0a-4fc2-9e77-a9de785ea269"},"outputs":[{"output_type":"display_data","data":{"text/plain":[""],"text/html":["\n"," \n"," "]},"metadata":{}}],"source":["# agroclimatic_zone = 'Eastern Plateau & Hills Region'\n","\n","# agroclimatic_zone = 'Lower Gangetic Plain Region'\n","\n","agroclimatic_zone = 'Middle Gangetic Plain Region'\n","\n","# agroclimatic_zone = 'Western Himalayan Region'\n","\n","# agroclimatic_zone = 'Eastern Himalayan Region'\n","\n","# agroclimatic_zone = 'Upper Gangetic Plain Region'\n","\n","# agroclimatic_zone = 'Trans Gangetic Plain Region'\n","\n","# agroclimatic_zone = 'Central Plateau & Hills Region'\n","\n","# agroclimatic_zone = 'Western Plateau and Hills Region'\n","\n","# agroclimatic_zone = 'Southern Plateau and Hills Region'\n","\n","# agroclimatic_zone = 'East Coast Plains & Hills Region'"]},{"cell_type":"code","execution_count":7,"metadata":{"colab":{"base_uri":"https://localhost:8080/","height":17},"executionInfo":{"elapsed":8,"status":"ok","timestamp":1775284614550,"user":{"displayName":"CoRE Stack","userId":"15795073488475210764"},"user_tz":-330},"id":"EpHcGapNX0cq","outputId":"cd3efa6e-0415-40bb-be10-31803077cad4"},"outputs":[{"output_type":"display_data","data":{"text/plain":[""],"text/html":["\n"," \n"," "]},"metadata":{}}],"source":["agroclimaticZone_acronym_dict = {'Eastern Plateau & Hills Region': 'EPAHR',\n"," 'Southern Plateau and Hills Region': 'SPAHR',\n"," 'East Coast Plains & Hills Region': 'ECPHR',\n"," 'Western Plateau and Hills Region': 'WPAHR',\n"," 'Central Plateau & Hills Region': 'CPAHR',\n"," 'Lower Gangetic Plain Region': 'LGPR',\n"," 'Middle Gangetic Plain Region': 'MGPR',\n"," 'Eastern Himalayan Region': 'EHR',\n"," 'Western Himalayan Region': 'WHR',\n"," 'Upper Gangetic Plain Region': 'UGPR',\n"," 'Trans Gangetic Plain Region': 'TGPR',\n"," 'West Coast Plains & Ghat Region': 'WCPGR',\n"," 'Gujarat Plains & Hills Region': 'GPHR',\n"," 'Western Dry Region': 'WDR'}"]},{"cell_type":"code","execution_count":null,"metadata":{"colab":{"base_uri":"https://localhost:8080/","height":17},"executionInfo":{"elapsed":4,"status":"ok","timestamp":1774369649861,"user":{"displayName":"CoRE Stack","userId":"15795073488475210764"},"user_tz":-330},"id":"qlIS9q3xZAzR","outputId":"e02201ba-b02f-4f7b-83df-53508912e2a1"},"outputs":[{"output_type":"display_data","data":{"text/plain":[""],"text/html":["\n"," \n"," "]},"metadata":{}}],"source":["# acz_list = ['Western Himalayan Region', 'Eastern Himalayan Region', 'Lower Gangetic Plain Region',\n","# 'Middle Gangetic Plain Region', 'Upper Gangetic Plain Region', 'Trans Gangetic Plain Region',\n","# 'Eastern Plateau & Hills Region', 'Central Plateau & Hills Region', 'Western Plateau and Hills Region',\n","# 'Southern Plateau and Hills Region', 'East Coast Plains & Hills Region']\n","\n","# acz_list = ['Eastern Plateau & Hills Region']"]},{"cell_type":"code","execution_count":8,"metadata":{"colab":{"base_uri":"https://localhost:8080/","height":17},"executionInfo":{"elapsed":6,"status":"ok","timestamp":1775284625250,"user":{"displayName":"CoRE Stack","userId":"15795073488475210764"},"user_tz":-330},"id":"j6ZwUG7Q-7WS","outputId":"989135a3-bb1b-477a-99c7-3a5916d759bb"},"outputs":[{"output_type":"display_data","data":{"text/plain":[""],"text/html":["\n"," \n"," "]},"metadata":{}}],"source":["def upload_file_to_gcs(local_file_path, file_name):\n"," blob = bucket.blob(f'nrm_tree_health/compiled_results/{file_name}.csv') # GCS path\n"," blob.upload_from_filename(local_file_path)\n","\n"," print(\"Upload complete.\")\n","\n","def export_to_gee(file_name):\n"," # CSV GCS path\n"," gcs_path = f'gs://core_stack/nrm_tree_health/compiled_results/{file_name}.csv'\n","\n"," # Create task ID and run table ingestion\n"," task_id = ee.data.newTaskId()[0]\n"," asset_id = f'projects/{ee_project}/assets/tree_characteristics/{file_name}'\n","\n"," manifest = {\n"," 'id': asset_id,\n"," 'sources': [\n"," {\n"," 'primaryPath': gcs_path,\n"," 'additionalPaths': []\n"," }\n"," ]\n"," }\n","\n"," ee.data.startTableIngestion(task_id, manifest)\n"," print(\"Ingestion task started:\", task_id)"]},{"cell_type":"code","execution_count":9,"metadata":{"colab":{"base_uri":"https://localhost:8080/","height":17},"collapsed":true,"executionInfo":{"elapsed":40,"status":"ok","timestamp":1775284627970,"user":{"displayName":"CoRE Stack","userId":"15795073488475210764"},"user_tz":-330},"id":"T7qHDbSCcy2H","outputId":"a8394995-d17d-44e1-c095-3add8ad846c5"},"outputs":[{"output_type":"display_data","data":{"text/plain":[""],"text/html":["\n"," \n"," "]},"metadata":{}}],"source":["# Set the years for which results need to be combined\n","years = ['2016','2017', '2018','2019','2020','2021','2022','2023','2024']"]},{"cell_type":"markdown","metadata":{"id":"_e3eXwraczpB"},"source":["# Combine CSVs CCD"]},{"cell_type":"code","execution_count":null,"metadata":{"colab":{"base_uri":"https://localhost:8080/","height":73},"executionInfo":{"elapsed":3235,"status":"ok","timestamp":1774369664051,"user":{"displayName":"CoRE Stack","userId":"15795073488475210764"},"user_tz":-330},"id":"cH7MCucjc4P8","outputId":"9ae54cac-8bc9-4727-edc0-224ef31a9a88"},"outputs":[{"output_type":"display_data","data":{"text/plain":[""],"text/html":["\n"," \n"," "]},"metadata":{}},{"output_type":"stream","name":"stdout","text":["119\n","['East Godavari', 'Srikakulam', 'Visakhapatnam', 'Vizianagaram', 'AurangabadB', 'Banka', 'Gaya', 'Jamui', 'Nawada', 'Rohtas', 'Baloda Bazar', 'Balod', 'BalrampurC', 'Bastar', 'Bemetara', 'BijapurC', 'BilaspurC', 'Dantewada', 'Dhamtari', 'Durg', 'Gariaband', 'Janjgir-Champa', 'Jashpur', 'Kabeerdham', 'Kondagaon', 'Korba', 'Koriya', 'Mahasamund', 'Mungeli', 'Narayanpur', 'Raigarh', 'Raipur', 'Rajnandgaon', 'Sukma', 'Surajpur', 'Surguja', 'Uttar Bastar Kanker', 'Bokaro', 'Chatra', 'Deoghar', 'Dhanbad', 'Dumka', 'Garhwa', 'Giridih', 'Godda', 'Gumla', 'Hazaribagh', 'Jamtara', 'Khunti', 'Kodarma', 'Latehar', 'Lohardaga', 'Pakur', 'Palamu', 'Pashchimi Singhbhum', 'Purbi Singhbhum', 'Ramgarh', 'Ranchi', 'Sahibganj', 'Saraikela-kharsawan', 'Simdega', 'Anuppur', 'Balaghat', 'Dindori', 'Jabalpur', 'Katni', 'Mandla', 'Rewa', 'Satna', 'Seoni', 'Shahdol', 'Sidhi', 'Singrauli', 'Umaria', 'Bhandara', 'Chandrapur', 'Garhchiroli', 'Gondiya', 'Nagpur', 'Wardha', 'Yavatmal', 'Anugul', 'Balangir', 'Baleshwar', 'Bargarh', 'Bauda', 'Bhadrak', 'Cuttack', 'Debagarh', 'Dhenkanal', 'Gajapati', 'Ganjam', 'Jajapur', 'Jharsuguda', 'Kalahandi', 'Kandhamal', 'Kendujhar', 'Koraput', 'Malkangiri', 'Mayurbhanj', 'Nabarangapur', 'Nayagarh', 'Nuapada', 'Rayagada', 'Sambalpur', 'Subarnapur', 'Sundargarh', 'Adilabad', 'Karimnagar', 'Khammam', 'Warangal', 'Mirzapur', 'Sonbhadra', 'Bankura', 'Barddhaman', 'Birbhum', 'Murshidabad', 'Pashchim Medinipur', 'Puruliya']\n"]}],"source":["df = pd.read_csv(f'drive/MyDrive/TreeHealth/Agroclimatic_regions/{agroclimatic_zone}.csv')\n","dist_list = list(df['Name'])\n","print(len(dist_list))\n","print(dist_list)"]},{"cell_type":"code","execution_count":null,"metadata":{"colab":{"base_uri":"https://localhost:8080/","height":1000},"executionInfo":{"elapsed":24132732,"status":"error","timestamp":1774393796785,"user":{"displayName":"CoRE Stack","userId":"15795073488475210764"},"user_tz":-330},"id":"9hFjUp4QmLL3","outputId":"401dc704-cf94-41f9-a38c-41c4c2b11d2a"},"outputs":[{"output_type":"display_data","data":{"text/plain":[""],"text/html":["\n"," \n"," "]},"metadata":{}},{"output_type":"stream","name":"stdout","text":["2023\n","0 East Godavari\n","1 Srikakulam\n","2 Visakhapatnam\n","3 Vizianagaram\n","4 AurangabadB\n","5 Banka\n","6 Gaya\n","7 Jamui\n","8 Nawada\n","9 Rohtas\n","10 Baloda Bazar\n","11 Balod\n","12 BalrampurC\n","13 Bastar\n","14 Bemetara\n","15 BijapurC\n","16 BilaspurC\n","17 Dantewada\n","18 Dhamtari\n","19 Durg\n","20 Gariaband\n","21 Janjgir-Champa\n","22 Jashpur\n","23 Kabeerdham\n","24 Kondagaon\n","25 Korba\n","26 Koriya\n","Saving result_0.csv with 80,497,091 rows\n","Upload complete.\n","Ingestion task started: c73487a3-3340-4ce0-afe7-141323f3c52a\n","27 Mahasamund\n","28 Mungeli\n","29 Narayanpur\n","30 Raigarh\n","31 Raipur\n","32 Rajnandgaon\n","33 Sukma\n","34 Surajpur\n","35 Surguja\n","36 Uttar Bastar Kanker\n","37 Bokaro\n","38 Chatra\n","39 Deoghar\n","40 Dhanbad\n","41 Dumka\n","42 Garhwa\n","43 Giridih\n","Saving result_1.csv with 80,298,124 rows\n"]},{"output_type":"stream","name":"stderr","text":["WARNING:google_auth_httplib2:httplib2 transport does not support per-request timeout. Set the timeout when constructing the httplib2.Http instance.\n"]},{"output_type":"stream","name":"stdout","text":["Upload complete.\n"]},{"output_type":"stream","name":"stderr","text":["WARNING:google_auth_httplib2:httplib2 transport does not support per-request timeout. Set the timeout when constructing the httplib2.Http instance.\n"]},{"output_type":"stream","name":"stdout","text":["Ingestion task started: 1469ba67-3aff-4e72-997c-39bbba391b60\n","44 Godda\n","45 Gumla\n","46 Hazaribagh\n","47 Jamtara\n","48 Khunti\n","49 Kodarma\n","50 Latehar\n","51 Lohardaga\n","52 Pakur\n","53 Palamu\n","54 Pashchimi Singhbhum\n","55 Purbi Singhbhum\n","56 Ramgarh\n","57 Ranchi\n","58 Sahibganj\n","59 Saraikela-kharsawan\n","60 Simdega\n","61 Anuppur\n","62 Balaghat\n","63 Dindori\n","64 Jabalpur\n","65 Katni\n","66 Mandla\n","67 Rewa\n","68 Satna\n","69 Seoni\n","70 Shahdol\n","71 Sidhi\n","Saving result_2.csv with 80,399,602 rows\n"]},{"output_type":"stream","name":"stderr","text":["WARNING:google_auth_httplib2:httplib2 transport does not support per-request timeout. Set the timeout when constructing the httplib2.Http instance.\n"]},{"output_type":"stream","name":"stdout","text":["Upload complete.\n"]},{"output_type":"stream","name":"stderr","text":["WARNING:google_auth_httplib2:httplib2 transport does not support per-request timeout. Set the timeout when constructing the httplib2.Http instance.\n"]},{"output_type":"stream","name":"stdout","text":["Ingestion task started: 17b547b5-7742-4840-806d-2fb7800bc9ce\n","72 Singrauli\n","73 Umaria\n","74 Bhandara\n","75 Chandrapur\n","76 Garhchiroli\n","77 Gondiya\n","78 Nagpur\n","79 Wardha\n","80 Yavatmal\n","81 Anugul\n","82 Balangir\n","83 Baleshwar\n","84 Bargarh\n","85 Bauda\n","86 Bhadrak\n","87 Cuttack\n","88 Debagarh\n","89 Dhenkanal\n","Saving result_3.csv with 80,196,991 rows\n"]},{"output_type":"stream","name":"stderr","text":["WARNING:google_auth_httplib2:httplib2 transport does not support per-request timeout. Set the timeout when constructing the httplib2.Http instance.\n"]},{"output_type":"stream","name":"stdout","text":["Upload complete.\n"]},{"output_type":"stream","name":"stderr","text":["WARNING:google_auth_httplib2:httplib2 transport does not support per-request timeout. Set the timeout when constructing the httplib2.Http instance.\n"]},{"output_type":"stream","name":"stdout","text":["Ingestion task started: ed0f5f3d-2f04-43b1-9f20-0b012f85218a\n","90 Gajapati\n","91 Ganjam\n","92 Jajapur\n","93 Jharsuguda\n","94 Kalahandi\n","95 Kandhamal\n","96 Kendujhar\n","97 Koraput\n","98 Malkangiri\n","99 Mayurbhanj\n","100 Nabarangapur\n","101 Nayagarh\n","102 Nuapada\n","103 Rayagada\n","Saving result_4.csv with 80,452,883 rows\n"]},{"output_type":"stream","name":"stderr","text":["WARNING:google_auth_httplib2:httplib2 transport does not support per-request timeout. Set the timeout when constructing the httplib2.Http instance.\n"]},{"output_type":"stream","name":"stdout","text":["Upload complete.\n"]},{"output_type":"stream","name":"stderr","text":["WARNING:google_auth_httplib2:httplib2 transport does not support per-request timeout. Set the timeout when constructing the httplib2.Http instance.\n"]},{"output_type":"stream","name":"stdout","text":["Ingestion task started: ae7633ed-a43e-4b3c-9074-3b3f223d2342\n","104 Sambalpur\n","105 Subarnapur\n","106 Sundargarh\n","107 Adilabad\n","108 Karimnagar\n","109 Khammam\n","110 Warangal\n","111 Mirzapur\n","112 Sonbhadra\n","113 Bankura\n","114 Barddhaman\n","115 Birbhum\n","116 Murshidabad\n","117 Pashchim Medinipur\n","118 Puruliya\n","Saving result_5.csv with 30,673,393 rows\n","Upload complete.\n","Ingestion task started: 1869d43e-71d7-49cc-9c1d-b1bb4a7f8467\n","2024\n","0 East Godavari\n","1 Srikakulam\n","2 Visakhapatnam\n","3 Vizianagaram\n","4 AurangabadB\n","5 Banka\n","6 Gaya\n","7 Jamui\n","8 Nawada\n","9 Rohtas\n","10 Baloda Bazar\n","11 Balod\n","12 BalrampurC\n","13 Bastar\n","14 Bemetara\n","15 BijapurC\n","16 BilaspurC\n","17 Dantewada\n","18 Dhamtari\n","19 Durg\n","20 Gariaband\n","21 Janjgir-Champa\n","22 Jashpur\n","23 Kabeerdham\n","24 Kondagaon\n","25 Korba\n","26 Koriya\n","Saving result_0.csv with 80,265,488 rows\n"]},{"output_type":"stream","name":"stderr","text":["WARNING:google_auth_httplib2:httplib2 transport does not support per-request timeout. Set the timeout when constructing the httplib2.Http instance.\n"]},{"output_type":"stream","name":"stdout","text":["Upload complete.\n"]},{"output_type":"stream","name":"stderr","text":["WARNING:google_auth_httplib2:httplib2 transport does not support per-request timeout. Set the timeout when constructing the httplib2.Http instance.\n"]},{"output_type":"stream","name":"stdout","text":["Ingestion task started: dafeda5b-16c0-423d-a117-240a643c4431\n","27 Mahasamund\n","28 Mungeli\n","29 Narayanpur\n","30 Raigarh\n","31 Raipur\n","32 Rajnandgaon\n","33 Sukma\n","34 Surajpur\n","35 Surguja\n","36 Uttar Bastar Kanker\n","37 Bokaro\n","38 Chatra\n","39 Deoghar\n","40 Dhanbad\n","41 Dumka\n","42 Garhwa\n","43 Giridih\n","Saving result_1.csv with 80,493,525 rows\n"]},{"output_type":"stream","name":"stderr","text":["WARNING:google_auth_httplib2:httplib2 transport does not support per-request timeout. Set the timeout when constructing the httplib2.Http instance.\n"]},{"output_type":"stream","name":"stdout","text":["Upload complete.\n"]},{"output_type":"stream","name":"stderr","text":["WARNING:google_auth_httplib2:httplib2 transport does not support per-request timeout. Set the timeout when constructing the httplib2.Http instance.\n"]},{"output_type":"stream","name":"stdout","text":["Ingestion task started: 248f4208-704a-4ba9-85d1-9bead9490c53\n","44 Godda\n","45 Gumla\n","46 Hazaribagh\n","47 Jamtara\n","48 Khunti\n","49 Kodarma\n","50 Latehar\n","51 Lohardaga\n","52 Pakur\n","53 Palamu\n","54 Pashchimi Singhbhum\n","55 Purbi Singhbhum\n","56 Ramgarh\n","57 Ranchi\n","58 Sahibganj\n","59 Saraikela-kharsawan\n","60 Simdega\n","61 Anuppur\n","62 Balaghat\n","63 Dindori\n","64 Jabalpur\n","65 Katni\n","66 Mandla\n","67 Rewa\n","68 Satna\n","69 Seoni\n","70 Shahdol\n","71 Sidhi\n","Saving result_2.csv with 80,264,095 rows\n","Upload complete.\n","Ingestion task started: 3cde4a32-1589-462e-b073-f84f97e6cd27\n"]},{"output_type":"error","ename":"OSError","evalue":"[Errno 5] Input/output error","traceback":["\u001b[0;31m---------------------------------------------------------------------------\u001b[0m","\u001b[0;31mOSError\u001b[0m Traceback (most recent call last)","\u001b[0;32m/usr/local/lib/python3.12/dist-packages/pandas/io/formats/csvs.py\u001b[0m in \u001b[0;36msave\u001b[0;34m(self)\u001b[0m\n\u001b[1;32m 269\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 270\u001b[0;31m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_save\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 271\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n","\u001b[0;32m/usr/local/lib/python3.12/dist-packages/pandas/io/formats/csvs.py\u001b[0m in \u001b[0;36m_save\u001b[0;34m(self)\u001b[0m\n\u001b[1;32m 274\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_save_header\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 275\u001b[0;31m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_save_body\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 276\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n","\u001b[0;32m/usr/local/lib/python3.12/dist-packages/pandas/io/formats/csvs.py\u001b[0m in \u001b[0;36m_save_body\u001b[0;34m(self)\u001b[0m\n\u001b[1;32m 312\u001b[0m \u001b[0;32mbreak\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 313\u001b[0;31m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_save_chunk\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mstart_i\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mend_i\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 314\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n","\u001b[0;32m/usr/local/lib/python3.12/dist-packages/pandas/io/formats/csvs.py\u001b[0m in \u001b[0;36m_save_chunk\u001b[0;34m(self, start_i, end_i)\u001b[0m\n\u001b[1;32m 323\u001b[0m \u001b[0mix\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mdata_index\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mslicer\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_get_values_for_csv\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m**\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_number_format\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 324\u001b[0;31m libwriters.write_csv_rows(\n\u001b[0m\u001b[1;32m 325\u001b[0m \u001b[0mdata\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n","\u001b[0;32mwriters.pyx\u001b[0m in \u001b[0;36mpandas._libs.writers.write_csv_rows\u001b[0;34m()\u001b[0m\n","\u001b[0;31mOSError\u001b[0m: [Errno 5] Input/output error","\nDuring handling of the above exception, another exception occurred:\n","\u001b[0;31mOSError\u001b[0m Traceback (most recent call last)","\u001b[0;31mOSError\u001b[0m: [Errno 5] Input/output error","\nDuring handling of the above exception, another exception occurred:\n","\u001b[0;31mOSError\u001b[0m Traceback (most recent call last)","\u001b[0;32m/tmp/ipykernel_2765/2038472130.py\u001b[0m in \u001b[0;36m\u001b[0;34m()\u001b[0m\n\u001b[1;32m 32\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 33\u001b[0m \u001b[0;31m# Append to current output file\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 34\u001b[0;31m df_chunk.to_csv(drive_path, mode='a',\n\u001b[0m\u001b[1;32m 35\u001b[0m header=first_write, index=False)\n\u001b[1;32m 36\u001b[0m \u001b[0mfirst_write\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;32mFalse\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n","\u001b[0;32m/usr/local/lib/python3.12/dist-packages/pandas/util/_decorators.py\u001b[0m in \u001b[0;36mwrapper\u001b[0;34m(*args, **kwargs)\u001b[0m\n\u001b[1;32m 331\u001b[0m \u001b[0mstacklevel\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mfind_stack_level\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 332\u001b[0m )\n\u001b[0;32m--> 333\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0mfunc\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m*\u001b[0m\u001b[0margs\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m**\u001b[0m\u001b[0mkwargs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 334\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 335\u001b[0m \u001b[0;31m# error: \"Callable[[VarArg(Any), KwArg(Any)], Any]\" has no\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n","\u001b[0;32m/usr/local/lib/python3.12/dist-packages/pandas/core/generic.py\u001b[0m in \u001b[0;36mto_csv\u001b[0;34m(self, path_or_buf, sep, na_rep, float_format, columns, header, index, index_label, mode, encoding, compression, quoting, quotechar, lineterminator, chunksize, date_format, doublequote, escapechar, decimal, errors, storage_options)\u001b[0m\n\u001b[1;32m 3965\u001b[0m )\n\u001b[1;32m 3966\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m-> 3967\u001b[0;31m return DataFrameRenderer(formatter).to_csv(\n\u001b[0m\u001b[1;32m 3968\u001b[0m \u001b[0mpath_or_buf\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 3969\u001b[0m \u001b[0mlineterminator\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mlineterminator\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n","\u001b[0;32m/usr/local/lib/python3.12/dist-packages/pandas/io/formats/format.py\u001b[0m in \u001b[0;36mto_csv\u001b[0;34m(self, path_or_buf, encoding, sep, columns, index_label, mode, compression, quoting, quotechar, lineterminator, chunksize, date_format, doublequote, escapechar, errors, storage_options)\u001b[0m\n\u001b[1;32m 1012\u001b[0m \u001b[0mformatter\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mfmt\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1013\u001b[0m )\n\u001b[0;32m-> 1014\u001b[0;31m \u001b[0mcsv_formatter\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0msave\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 1015\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1016\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mcreated_buffer\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n","\u001b[0;32m/usr/local/lib/python3.12/dist-packages/pandas/io/formats/csvs.py\u001b[0m in \u001b[0;36msave\u001b[0;34m(self)\u001b[0m\n\u001b[1;32m 249\u001b[0m \"\"\"\n\u001b[1;32m 250\u001b[0m \u001b[0;31m# apply compression and byte/text conversion\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 251\u001b[0;31m with get_handle(\n\u001b[0m\u001b[1;32m 252\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mfilepath_or_buffer\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 253\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mmode\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n","\u001b[0;32m/usr/local/lib/python3.12/dist-packages/pandas/io/common.py\u001b[0m in \u001b[0;36m__exit__\u001b[0;34m(self, exc_type, exc_value, traceback)\u001b[0m\n\u001b[1;32m 155\u001b[0m \u001b[0mtraceback\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0mTracebackType\u001b[0m \u001b[0;34m|\u001b[0m \u001b[0;32mNone\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 156\u001b[0m ) -> None:\n\u001b[0;32m--> 157\u001b[0;31m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mclose\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 158\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 159\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n","\u001b[0;32m/usr/local/lib/python3.12/dist-packages/pandas/io/common.py\u001b[0m in \u001b[0;36mclose\u001b[0;34m(self)\u001b[0m\n\u001b[1;32m 142\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mcreated_handles\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mremove\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mhandle\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 143\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mhandle\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mcreated_handles\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 144\u001b[0;31m \u001b[0mhandle\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mclose\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 145\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mcreated_handles\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m[\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 146\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mis_wrapped\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;32mFalse\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n","\u001b[0;31mOSError\u001b[0m: [Errno 5] Input/output error"]}],"source":["best_month = best_month_dict[agroclimatic_zone]\n","chunk_size=500_000\n","max_rows=80_000_000\n","\n","for year in years:\n"," print(year)\n"," result_num = 0\n"," dst_num = 0\n"," row_counter = 0\n","\n"," path = f'drive/MyDrive/TreeHealth/{agroclimatic_zone}/Compiled Results/{year}/'\n"," os.makedirs(path, exist_ok=True)\n"," drive_path = f\"{path}/result_{result_num}.csv\"\n"," first_write = True # controls header\n","\n"," for district in dist_list:\n"," print(dst_num, district)\n"," dst_num += 1\n"," try:\n"," reader = pd.read_csv(\n"," f'drive/MyDrive/TreeHealth/{agroclimatic_zone}/{district}/{year}/result_monthly_cc.csv',\n"," chunksize=chunk_size\n"," )\n"," except:\n"," continue\n","\n"," for df_chunk in reader:\n"," if df_chunk.shape[0] > 0:\n"," # print(df_chunk)\n"," # Add \"cc\" column from best month\n"," df_chunk['cc'] = df_chunk[best_month]\n","\n"," # Append to current output file\n"," df_chunk.to_csv(drive_path, mode='a',\n"," header=first_write, index=False)\n"," first_write = False\n","\n"," row_counter += len(df_chunk)\n","\n"," # Split file if max_rows exceeded\n"," if row_counter >= max_rows:\n"," print(f'Saving result_{result_num}.csv with {row_counter:,} rows')\n","\n"," file_name = f\"ccd_{year}_result_{result_num}_{agroclimaticZone_acronym_dict[agroclimatic_zone]}\"\n"," upload_file_to_gcs(drive_path, file_name)\n"," export_to_gee(file_name)\n","\n"," # reset counters\n"," result_num += 1\n"," row_counter = 0\n"," drive_path = f\"{path}/result_{result_num}.csv\"\n"," first_write = True # new file needs header\n","\n"," # flush leftovers\n"," if row_counter > 0:\n"," print(f'Saving result_{result_num}.csv with {row_counter:,} rows')\n"," file_name = f\"ccd_{year}_result_{result_num}_{agroclimaticZone_acronym_dict[agroclimatic_zone]}\"\n"," upload_file_to_gcs(drive_path, file_name)\n"," export_to_gee(file_name)\n"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"RYYNSioqd0T9"},"outputs":[],"source":["# best_month = best_month_dict[agroclimatic_zone]\n","# for year in years:\n","# print(year)\n","# df_con = pd.DataFrame()\n","# df_len = 0\n","# result_num = 0\n","# dst_num = 0\n","# for district in dist_list:\n","# print(dst_num, district)\n","# dst_num += 1\n","# try:\n","# df = pd.read_csv(f'drive/MyDrive/TreeHealth/{agroclimatic_zone}/{district}/{year}/result_monthly_cc.csv')\n","# except:\n","# continue\n","# df_len += len(df)\n","# df_con = pd.concat([df_con, df])\n","# del(df)\n","# print(df_len)\n","\n","\n","# if df_len > 80000000:\n","# print(f'Saving result_{result_num}.csv')\n","# df_con['cc'] = df_con[best_month]\n","# path = f'drive/MyDrive/TreeHealth/{agroclimatic_zone}/Compiled Results/{year}/'\n","# if not os.path.exists(path):\n","# os.makedirs(path)\n","# drive_path = f'{path}/result_{result_num}.csv'\n","# df_con.to_csv(f'drive/MyDrive/TreeHealth/{agroclimatic_zone}/Compiled Results/{year}/result_{result_num}.csv', index=False)\n","# file_name = f\"ccd_{year}_result_{result_num}_{agroclimaticZone_acronym_dict[agroclimatic_zone]}\"\n","# upload_file_to_gcs(drive_path, file_name)\n","# export_to_gee(file_name)\n","\n","# result_num += 1\n","# del(df_con)\n","# df_len = 0\n","# df_con = pd.DataFrame()\n","\n","# if (len(df_con) > 0):\n","# print(f'Saving result_{result_num}.csv')\n","# df_con['cc'] = df_con[best_month]\n","# path = f'drive/MyDrive/TreeHealth/{agroclimatic_zone}/Compiled Results/{year}/'\n","# if not os.path.exists(path):\n","# os.makedirs(path)\n","# drive_path = f'{path}/result_{result_num}.csv'\n","# df_con.to_csv(drive_path, index=False)\n","\n","# file_name = f\"ccd_{year}_result_{result_num}_{agroclimaticZone_acronym_dict[agroclimatic_zone]}\"\n","# upload_file_to_gcs(drive_path, file_name)\n","# export_to_gee(file_name)\n","\n","# result_num += 1\n","# del(df_con)\n","# df_len = 0\n","# df_con = pd.DataFrame()"]},{"cell_type":"code","execution_count":null,"metadata":{"colab":{"base_uri":"https://localhost:8080/","height":327},"executionInfo":{"elapsed":168191,"status":"ok","timestamp":1762767949388,"user":{"displayName":"CoRE Stack","userId":"15795073488475210764"},"user_tz":-330},"id":"7nY3jBPsNG6_","outputId":"23307a31-72c0-48fe-8962-7cce2c8cf161"},"outputs":[{"data":{"text/html":["\n"," \n"," "],"text/plain":[""]},"metadata":{},"output_type":"display_data"},{"name":"stdout","output_type":"stream","text":[".geo object\n","cc_1 int64\n","cc_2 int64\n","cc_3 int64\n","cc_4 int64\n","cc_5 int64\n","cc_6 int64\n","cc_7 int64\n","cc_8 int64\n","cc_9 int64\n","cc_10 int64\n","cc_11 int64\n","cc_12 int64\n","cc int64\n","dtype: object\n","Upload complete.\n","Ingestion task started: 2a4f86c7-03b8-4605-916d-fb63feae926e\n"]}],"source":["# year = '2016'\n","# path = f'drive/MyDrive/TreeHealth/{agroclimatic_zone}/Compiled Results/{year}/'\n","# for result_num in range(1):\n","# drive_path = f\"{path}/result_{result_num}.csv\"\n","# reader = pd.read_csv(drive_path, chunksize=100)\n","# for df_chunk in reader:\n","# print(df_chunk.dtypes)\n","# break\n","# # upload + export\n","# upload_file_to_gcs(\n","# drive_path,\n","# f\"ccd_{year}_result_{result_num}_{agroclimaticZone_acronym_dict[agroclimatic_zone]}\"\n","# )\n","# export_to_gee(\n","# f\"ccd_{year}_result_{result_num}_{agroclimaticZone_acronym_dict[agroclimatic_zone]}\"\n","# )"]},{"cell_type":"markdown","metadata":{"id":"sfozGeDdiQcC"},"source":["# Combine CSVs CH"]},{"cell_type":"code","execution_count":10,"metadata":{"colab":{"base_uri":"https://localhost:8080/","height":17},"executionInfo":{"elapsed":2344,"status":"ok","timestamp":1775284632488,"user":{"displayName":"CoRE Stack","userId":"15795073488475210764"},"user_tz":-330},"id":"bk7terDQiQ_y","outputId":"8ab1da5e-de1e-4284-e3a6-a81a26266423"},"outputs":[{"output_type":"display_data","data":{"text/plain":[""],"text/html":["\n"," \n"," "]},"metadata":{}}],"source":["df = pd.read_csv('drive/MyDrive/TreeHealth/district_to_agroclimaticZone_mapping.csv')"]},{"cell_type":"code","execution_count":11,"metadata":{"colab":{"base_uri":"https://localhost:8080/","height":17},"executionInfo":{"elapsed":3,"status":"ok","timestamp":1775284632493,"user":{"displayName":"CoRE Stack","userId":"15795073488475210764"},"user_tz":-330},"id":"eQgVTZCiiYa0","outputId":"83e3c7a2-8256-4c9b-b076-9f3ce2cd84f6"},"outputs":[{"output_type":"display_data","data":{"text/plain":[""],"text/html":["\n"," \n"," "]},"metadata":{}}],"source":["# Function to convert string representation of list to an actual list\n","def convert_to_list(string):\n"," return ast.literal_eval(string)\n","\n","df['IntersectingZones'] = df['IntersectingZones'].apply(convert_to_list)"]},{"cell_type":"code","execution_count":12,"metadata":{"colab":{"base_uri":"https://localhost:8080/","height":17},"executionInfo":{"elapsed":23,"status":"ok","timestamp":1775284633301,"user":{"displayName":"CoRE Stack","userId":"15795073488475210764"},"user_tz":-330},"id":"QxHpT4hhibUG","outputId":"03c65e28-8dfa-4dfc-eec2-b1eb3268f2b2"},"outputs":[{"output_type":"display_data","data":{"text/plain":[""],"text/html":["\n"," \n"," "]},"metadata":{}}],"source":["district_mapping_df = df[df['AgroclimaticZone'] == agroclimatic_zone][['District', 'IntersectingZones']]"]},{"cell_type":"code","execution_count":13,"metadata":{"colab":{"base_uri":"https://localhost:8080/","height":55},"executionInfo":{"elapsed":15,"status":"ok","timestamp":1775284634739,"user":{"displayName":"CoRE Stack","userId":"15795073488475210764"},"user_tz":-330},"id":"PpR3DJEbifWe","outputId":"9a95daed-af5c-4426-c099-363db40b89a3"},"outputs":[{"output_type":"display_data","data":{"text/plain":[""],"text/html":["\n"," \n"," "]},"metadata":{}},{"output_type":"stream","name":"stdout","text":["['Araria', 'Arwal', 'AurangabadB', 'Banka', 'Begusarai', 'Bhagalpur', 'Bhojpur', 'Buxar', 'Darbhanga', 'Gaya', 'Gopalganj', 'Jamui', 'Jehanabad', 'Kaimur', 'Katihar', 'Khagaria', 'Kishanganj', 'Lakhisarai', 'Madhepura', 'Madhubani', 'Munger', 'Muzaffarpur', 'Nalanda', 'Nawada', 'Pashchim Champaran', 'Patna', 'Purba Champaran', 'Purnia', 'Rohtas', 'Saharsa', 'Samastipur', 'Saran', 'Sheikhpura', 'Sheohar', 'Sitamarhi', 'Siwan', 'Supaul', 'Vaishali', 'Godda', 'Sahibganj', 'Ambedkar Nagar', 'Azamgarh', 'Bahraich', 'Ballia', 'Balrampur', 'Basti', 'Chandauli', 'Deoria', 'Faizabad', 'Ghazipur', 'Gonda', 'Gorakhpur', 'Jaunpur', 'Kushinagar', 'Maharajganj', 'Mau', 'Mirzapur', 'Sant Kabir Nagar', 'Sant Ravi Das Nagar', 'Shravasti', 'Siddharth Nagar', 'Sonbhadra', 'Varanasi']\n"]}],"source":["dist_list = []\n","i = 0\n","for ind in district_mapping_df.index:\n"," district = district_mapping_df.loc[ind, 'District']\n"," zones = district_mapping_df['IntersectingZones'][ind]\n"," # print(i, district, zones)\n"," dist_list.append(district)\n"," i += 1\n","# dist_list = ['Darjiling']\n","print(dist_list)"]},{"cell_type":"code","execution_count":14,"metadata":{"colab":{"base_uri":"https://localhost:8080/","height":17},"executionInfo":{"elapsed":7,"status":"ok","timestamp":1775284642451,"user":{"displayName":"CoRE Stack","userId":"15795073488475210764"},"user_tz":-330},"id":"orS9dnFnmVEe","outputId":"9237665f-2f26-4e37-e4af-185bbc58fbbb"},"outputs":[{"output_type":"display_data","data":{"text/plain":[""],"text/html":["\n"," \n"," "]},"metadata":{}}],"source":["def classify_chunk(df_chunk):\n"," \"\"\"Add ch_class column based on rh50/rh75/rh98 logic.\"\"\"\n"," conditions = [\n"," (df_chunk['rh50_class'] == 0) & (df_chunk['rh75_class'] == 0) & (df_chunk['rh98_class'] == 0),\n"," (df_chunk['rh50_class'] == 0) & (df_chunk['rh75_class'] == 0) & (df_chunk['rh98_class'] == 1),\n"," (df_chunk['rh50_class'] == 0) & (df_chunk['rh75_class'] == 1) & (df_chunk['rh98_class'] == 0),\n"," (df_chunk['rh50_class'] == 0) & (df_chunk['rh75_class'] == 1) & (df_chunk['rh98_class'] == 1),\n"," (df_chunk['rh50_class'] == 1) & (df_chunk['rh75_class'] == 0) & (df_chunk['rh98_class'] == 0),\n"," (df_chunk['rh50_class'] == 1) & (df_chunk['rh75_class'] == 0) & (df_chunk['rh98_class'] == 1)\n"," ]\n"," choices = [0, 0, 1, 2, 1, 2]\n","\n"," df_chunk['ch_class'] = np.select(conditions, choices, default=3)\n"," return df_chunk"]},{"cell_type":"code","execution_count":15,"metadata":{"colab":{"base_uri":"https://localhost:8080/","height":1000},"id":"PbwYc1iLmXA5","executionInfo":{"status":"error","timestamp":1775286506270,"user_tz":-330,"elapsed":1861463,"user":{"displayName":"CoRE Stack","userId":"15795073488475210764"}},"outputId":"70821778-37d8-4b6a-c59a-304c8f89d767"},"outputs":[{"output_type":"display_data","data":{"text/plain":[""],"text/html":["\n"," \n"," "]},"metadata":{}},{"output_type":"stream","name":"stdout","text":["2016\n","0 Araria\n","1 Arwal\n","2 AurangabadB\n","3 Banka\n","4 Begusarai\n","5 Bhagalpur\n","6 Bhojpur\n","7 Buxar\n","8 Darbhanga\n","9 Gaya\n","10 Gopalganj\n","11 Jamui\n","12 Jehanabad\n","13 Kaimur\n","14 Katihar\n","15 Khagaria\n","16 Kishanganj\n","17 Lakhisarai\n","18 Madhepura\n","19 Madhubani\n","20 Munger\n","21 Muzaffarpur\n","22 Nalanda\n","23 Nawada\n","24 Pashchim Champaran\n","25 Patna\n","26 Purba Champaran\n","27 Purnia\n","28 Rohtas\n","29 Saharsa\n","30 Samastipur\n","31 Saran\n","32 Sheikhpura\n","33 Sheohar\n","34 Sitamarhi\n","35 Siwan\n","36 Supaul\n","37 Vaishali\n","38 Godda\n","39 Sahibganj\n","40 Ambedkar Nagar\n","41 Azamgarh\n","42 Bahraich\n","43 Ballia\n","44 Balrampur\n","45 Basti\n","46 Chandauli\n","47 Deoria\n","48 Faizabad\n","49 Ghazipur\n","50 Gonda\n","51 Gorakhpur\n","52 Jaunpur\n","53 Kushinagar\n","54 Maharajganj\n","55 Mau\n","56 Mirzapur\n","57 Sant Kabir Nagar\n","58 Sant Ravi Das Nagar\n","59 Shravasti\n","60 Siddharth Nagar\n","61 Sonbhadra\n","62 Varanasi\n","Saving result_0.csv with 68,660,369 rows\n"]},{"output_type":"error","ename":"KeyboardInterrupt","evalue":"","traceback":["\u001b[0;31m---------------------------------------------------------------------------\u001b[0m","\u001b[0;31mKeyboardInterrupt\u001b[0m Traceback (most recent call last)","\u001b[0;32m/tmp/ipykernel_4886/2152513440.py\u001b[0m in \u001b[0;36m\u001b[0;34m()\u001b[0m\n\u001b[1;32m 56\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mrow_counter\u001b[0m \u001b[0;34m>\u001b[0m \u001b[0;36m0\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 57\u001b[0m \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34mf'Saving result_{result_num}.csv with {row_counter:,} rows'\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 58\u001b[0;31m upload_file_to_gcs(\n\u001b[0m\u001b[1;32m 59\u001b[0m \u001b[0mdrive_path\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 60\u001b[0m \u001b[0;34mf\"ch_{year}_result_{result_num}_{agroclimaticZone_acronym_dict[agroclimatic_zone]}\"\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n","\u001b[0;32m/tmp/ipykernel_4886/1786300172.py\u001b[0m in \u001b[0;36mupload_file_to_gcs\u001b[0;34m(local_file_path, file_name)\u001b[0m\n\u001b[1;32m 1\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0mupload_file_to_gcs\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mlocal_file_path\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mfile_name\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 2\u001b[0m \u001b[0mblob\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mbucket\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mblob\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34mf'nrm_tree_health/compiled_results/{file_name}.csv'\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;31m# GCS path\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 3\u001b[0;31m \u001b[0mblob\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mupload_from_filename\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mlocal_file_path\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 4\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 5\u001b[0m \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"Upload complete.\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n","\u001b[0;32m/usr/local/lib/python3.12/dist-packages/google/cloud/storage/blob.py\u001b[0m in \u001b[0;36mupload_from_filename\u001b[0;34m(self, filename, content_type, client, predefined_acl, if_generation_match, if_generation_not_match, if_metageneration_match, if_metageneration_not_match, timeout, checksum, retry, crc32c_checksum_value)\u001b[0m\n\u001b[1;32m 3196\u001b[0m \"\"\"\n\u001b[1;32m 3197\u001b[0m \u001b[0;32mwith\u001b[0m \u001b[0mcreate_trace_span\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mname\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;34m\"Storage.Blob.uploadFromFilename\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m-> 3198\u001b[0;31m self._handle_filename_and_upload(\n\u001b[0m\u001b[1;32m 3199\u001b[0m \u001b[0mfilename\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 3200\u001b[0m \u001b[0mcontent_type\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mcontent_type\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n","\u001b[0;32m/usr/local/lib/python3.12/dist-packages/google/cloud/storage/blob.py\u001b[0m in \u001b[0;36m_handle_filename_and_upload\u001b[0;34m(self, filename, content_type, *args, **kwargs)\u001b[0m\n\u001b[1;32m 3046\u001b[0m \u001b[0;32mwith\u001b[0m \u001b[0mopen\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mfilename\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m\"rb\"\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;32mas\u001b[0m \u001b[0mfile_obj\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 3047\u001b[0m \u001b[0mtotal_bytes\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mos\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mfstat\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mfile_obj\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mfileno\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mst_size\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m-> 3048\u001b[0;31m self._prep_and_do_upload(\n\u001b[0m\u001b[1;32m 3049\u001b[0m \u001b[0mfile_obj\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 3050\u001b[0m \u001b[0mcontent_type\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mcontent_type\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n","\u001b[0;32m/usr/local/lib/python3.12/dist-packages/google/cloud/storage/blob.py\u001b[0m in \u001b[0;36m_prep_and_do_upload\u001b[0;34m(self, file_obj, rewind, size, content_type, client, predefined_acl, if_generation_match, if_generation_not_match, if_metageneration_match, if_metageneration_not_match, timeout, checksum, retry, command, crc32c_checksum_value)\u001b[0m\n\u001b[1;32m 2833\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 2834\u001b[0m \u001b[0;32mtry\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m-> 2835\u001b[0;31m created_json = self._do_upload(\n\u001b[0m\u001b[1;32m 2836\u001b[0m \u001b[0mclient\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 2837\u001b[0m \u001b[0mfile_obj\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n","\u001b[0;32m/usr/local/lib/python3.12/dist-packages/google/cloud/storage/blob.py\u001b[0m in \u001b[0;36m_do_upload\u001b[0;34m(self, client, stream, content_type, size, predefined_acl, if_generation_match, if_generation_not_match, if_metageneration_match, if_metageneration_not_match, timeout, checksum, retry, command, crc32c_checksum_value)\u001b[0m\n\u001b[1;32m 2644\u001b[0m )\n\u001b[1;32m 2645\u001b[0m \u001b[0;32melse\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m-> 2646\u001b[0;31m response = self._do_resumable_upload(\n\u001b[0m\u001b[1;32m 2647\u001b[0m \u001b[0mclient\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 2648\u001b[0m \u001b[0mstream\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n","\u001b[0;32m/usr/local/lib/python3.12/dist-packages/google/cloud/storage/blob.py\u001b[0m in \u001b[0;36m_do_resumable_upload\u001b[0;34m(self, client, stream, content_type, size, predefined_acl, if_generation_match, if_generation_not_match, if_metageneration_match, if_metageneration_not_match, timeout, checksum, retry, command, crc32c_checksum_value)\u001b[0m\n\u001b[1;32m 2461\u001b[0m \u001b[0;32mwhile\u001b[0m \u001b[0;32mnot\u001b[0m \u001b[0mupload\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mfinished\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 2462\u001b[0m \u001b[0;32mtry\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m-> 2463\u001b[0;31m \u001b[0mresponse\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mupload\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mtransmit_next_chunk\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mtransport\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mtimeout\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mtimeout\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 2464\u001b[0m \u001b[0;32mexcept\u001b[0m \u001b[0mDataCorruption\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 2465\u001b[0m \u001b[0;31m# Attempt to delete the corrupted object.\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n","\u001b[0;32m/usr/local/lib/python3.12/dist-packages/google/cloud/storage/_media/requests/upload.py\u001b[0m in \u001b[0;36mtransmit_next_chunk\u001b[0;34m(self, transport, timeout)\u001b[0m\n\u001b[1;32m 527\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mresult\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 528\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 529\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0m_request_helpers\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mwait_and_retry\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mretriable_request\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_retry_strategy\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 530\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 531\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0mrecover\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mtransport\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n","\u001b[0;32m/usr/local/lib/python3.12/dist-packages/google/cloud/storage/_media/requests/_request_helpers.py\u001b[0m in \u001b[0;36mwait_and_retry\u001b[0;34m(func, retry_strategy)\u001b[0m\n\u001b[1;32m 105\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mretry_strategy\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 106\u001b[0m \u001b[0mfunc\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mretry_strategy\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mfunc\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 107\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0mfunc\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m","\u001b[0;32m/usr/local/lib/python3.12/dist-packages/google/api_core/retry/retry_unary.py\u001b[0m in \u001b[0;36mretry_wrapped_func\u001b[0;34m(*args, **kwargs)\u001b[0m\n\u001b[1;32m 292\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_initial\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_maximum\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mmultiplier\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_multiplier\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 293\u001b[0m )\n\u001b[0;32m--> 294\u001b[0;31m return retry_target(\n\u001b[0m\u001b[1;32m 295\u001b[0m \u001b[0mtarget\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 296\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_predicate\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n","\u001b[0;32m/usr/local/lib/python3.12/dist-packages/google/api_core/retry/retry_unary.py\u001b[0m in \u001b[0;36mretry_target\u001b[0;34m(target, predicate, sleep_generator, timeout, on_error, exception_factory, **kwargs)\u001b[0m\n\u001b[1;32m 145\u001b[0m \u001b[0;32mwhile\u001b[0m \u001b[0;32mTrue\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 146\u001b[0m \u001b[0;32mtry\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 147\u001b[0;31m \u001b[0mresult\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mtarget\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 148\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0minspect\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0misawaitable\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mresult\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 149\u001b[0m \u001b[0mwarnings\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mwarn\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0m_ASYNC_RETRY_WARNING\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n","\u001b[0;32m/usr/local/lib/python3.12/dist-packages/google/cloud/storage/_media/requests/upload.py\u001b[0m in \u001b[0;36mretriable_request\u001b[0;34m()\u001b[0m\n\u001b[1;32m 519\u001b[0m \u001b[0;31m# Wrap the request business logic in a function to be retried.\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 520\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0mretriable_request\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 521\u001b[0;31m result = transport.request(\n\u001b[0m\u001b[1;32m 522\u001b[0m \u001b[0mmethod\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0murl\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mdata\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mpayload\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mheaders\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mheaders\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mtimeout\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mtimeout\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 523\u001b[0m )\n","\u001b[0;32m/usr/local/lib/python3.12/dist-packages/google/auth/transport/requests.py\u001b[0m in \u001b[0;36mrequest\u001b[0;34m(self, method, url, data, headers, max_allowed_time, timeout, **kwargs)\u001b[0m\n\u001b[1;32m 541\u001b[0m \u001b[0;32mwith\u001b[0m \u001b[0mTimeoutGuard\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mremaining_time\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;32mas\u001b[0m \u001b[0mguard\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 542\u001b[0m \u001b[0m_helpers\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mrequest_log\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0m_LOGGER\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mmethod\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0murl\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mdata\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mheaders\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 543\u001b[0;31m response = super(AuthorizedSession, self).request(\n\u001b[0m\u001b[1;32m 544\u001b[0m \u001b[0mmethod\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 545\u001b[0m \u001b[0murl\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n","\u001b[0;32m/usr/local/lib/python3.12/dist-packages/requests/sessions.py\u001b[0m in \u001b[0;36mrequest\u001b[0;34m(self, method, url, params, data, headers, cookies, files, auth, timeout, allow_redirects, proxies, hooks, stream, verify, cert, json)\u001b[0m\n\u001b[1;32m 587\u001b[0m }\n\u001b[1;32m 588\u001b[0m \u001b[0msend_kwargs\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mupdate\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0msettings\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 589\u001b[0;31m \u001b[0mresp\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0msend\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mprep\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m**\u001b[0m\u001b[0msend_kwargs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 590\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 591\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mresp\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n","\u001b[0;32m/usr/local/lib/python3.12/dist-packages/requests/sessions.py\u001b[0m in \u001b[0;36msend\u001b[0;34m(self, request, **kwargs)\u001b[0m\n\u001b[1;32m 701\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 702\u001b[0m \u001b[0;31m# Send the request\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 703\u001b[0;31m \u001b[0mr\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0madapter\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0msend\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mrequest\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m**\u001b[0m\u001b[0mkwargs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 704\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 705\u001b[0m \u001b[0;31m# Total elapsed time of the request (approximately)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n","\u001b[0;32m/usr/local/lib/python3.12/dist-packages/requests/adapters.py\u001b[0m in \u001b[0;36msend\u001b[0;34m(self, request, stream, timeout, verify, cert, proxies)\u001b[0m\n\u001b[1;32m 665\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 666\u001b[0m \u001b[0;32mtry\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 667\u001b[0;31m resp = conn.urlopen(\n\u001b[0m\u001b[1;32m 668\u001b[0m \u001b[0mmethod\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mrequest\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mmethod\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 669\u001b[0m \u001b[0murl\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0murl\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n","\u001b[0;32m/usr/local/lib/python3.12/dist-packages/urllib3/connectionpool.py\u001b[0m in \u001b[0;36murlopen\u001b[0;34m(self, method, url, body, headers, retries, redirect, assert_same_host, timeout, pool_timeout, release_conn, chunked, body_pos, preload_content, decode_content, **response_kw)\u001b[0m\n\u001b[1;32m 785\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 786\u001b[0m \u001b[0;31m# Make the request on the HTTPConnection object\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 787\u001b[0;31m response = self._make_request(\n\u001b[0m\u001b[1;32m 788\u001b[0m \u001b[0mconn\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 789\u001b[0m \u001b[0mmethod\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n","\u001b[0;32m/usr/local/lib/python3.12/dist-packages/urllib3/connectionpool.py\u001b[0m in \u001b[0;36m_make_request\u001b[0;34m(self, conn, method, url, body, headers, retries, timeout, chunked, response_conn, preload_content, decode_content, enforce_content_length)\u001b[0m\n\u001b[1;32m 491\u001b[0m \u001b[0;31m# urllib3.request. It also calls makefile (recv) on the socket.\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 492\u001b[0m \u001b[0;32mtry\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 493\u001b[0;31m conn.request(\n\u001b[0m\u001b[1;32m 494\u001b[0m \u001b[0mmethod\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 495\u001b[0m \u001b[0murl\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n","\u001b[0;32m/usr/local/lib/python3.12/dist-packages/urllib3/connection.py\u001b[0m in \u001b[0;36mrequest\u001b[0;34m(self, method, url, body, headers, chunked, preload_content, decode_content, enforce_content_length)\u001b[0m\n\u001b[1;32m 506\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0msend\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34mb\"%x\\r\\n%b\\r\\n\"\u001b[0m \u001b[0;34m%\u001b[0m \u001b[0;34m(\u001b[0m\u001b[0mlen\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mchunk\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mchunk\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 507\u001b[0m \u001b[0;32melse\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 508\u001b[0;31m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0msend\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mchunk\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 509\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 510\u001b[0m \u001b[0;31m# Regardless of whether we have a body or not, if we're in\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n","\u001b[0;32m/usr/lib/python3.12/http/client.py\u001b[0m in \u001b[0;36msend\u001b[0;34m(self, data)\u001b[0m\n\u001b[1;32m 1075\u001b[0m \u001b[0msys\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0maudit\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"http.client.send\"\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mdata\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1076\u001b[0m \u001b[0;32mtry\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m-> 1077\u001b[0;31m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0msock\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0msendall\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mdata\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 1078\u001b[0m \u001b[0;32mexcept\u001b[0m \u001b[0mTypeError\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1079\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0misinstance\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mdata\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mcollections\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mabc\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mIterable\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n","\u001b[0;32m/usr/lib/python3.12/ssl.py\u001b[0m in \u001b[0;36msendall\u001b[0;34m(self, data, flags)\u001b[0m\n\u001b[1;32m 1208\u001b[0m \u001b[0mamount\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mlen\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mbyte_view\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1209\u001b[0m \u001b[0;32mwhile\u001b[0m \u001b[0mcount\u001b[0m \u001b[0;34m<\u001b[0m \u001b[0mamount\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m-> 1210\u001b[0;31m \u001b[0mv\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0msend\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mbyte_view\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mcount\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 1211\u001b[0m \u001b[0mcount\u001b[0m \u001b[0;34m+=\u001b[0m \u001b[0mv\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1212\u001b[0m \u001b[0;32melse\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n","\u001b[0;32m/usr/lib/python3.12/ssl.py\u001b[0m in \u001b[0;36msend\u001b[0;34m(self, data, flags)\u001b[0m\n\u001b[1;32m 1177\u001b[0m \u001b[0;34m\"non-zero flags not allowed in calls to send() on %s\"\u001b[0m \u001b[0;34m%\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1178\u001b[0m self.__class__)\n\u001b[0;32m-> 1179\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_sslobj\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mwrite\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mdata\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 1180\u001b[0m \u001b[0;32melse\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1181\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0msuper\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0msend\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mdata\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mflags\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n","\u001b[0;31mKeyboardInterrupt\u001b[0m: "]}],"source":["chunk_size=500_000\n","max_rows=80_000_000\n","for year in years:\n"," print(year)\n"," result_num = 0\n"," dst_num = 0\n"," row_counter = 0\n","\n"," path = f'drive/MyDrive/TreeHealth/{agroclimatic_zone}/Compiled Results CH/{year}/'\n"," os.makedirs(path, exist_ok=True)\n"," drive_path = f\"{path}/result_{result_num}.csv\"\n"," first_write = True # controls header writing\n","\n"," for district in dist_list:\n"," print(dst_num, district)\n"," dst_num += 1\n"," try:\n"," reader = pd.read_csv(\n"," f'drive/MyDrive/TreeHealth/{agroclimatic_zone}/{district}/{year}/result_chm.csv',\n"," chunksize=chunk_size\n"," )\n"," except:\n"," print(\"Errorrrrrrrr\")\n"," continue\n","\n"," for df_chunk in reader:\n"," df_chunk = classify_chunk(df_chunk)\n","\n"," # Append to current result file\n"," df_chunk.to_csv(drive_path, mode='a',\n"," header=first_write, index=False)\n"," first_write = False\n","\n"," row_counter += len(df_chunk)\n","\n"," # If exceeds limit → finalize file and start new one\n"," if row_counter >= max_rows:\n"," print(f'Saving result_{result_num}.csv with {row_counter:,} rows')\n","\n"," # upload + export\n"," upload_file_to_gcs(\n"," drive_path,\n"," f\"ch_{year}_result_{result_num}_{agroclimaticZone_acronym_dict[agroclimatic_zone]}\"\n"," )\n"," export_to_gee(\n"," f\"ch_{year}_result_{result_num}_{agroclimaticZone_acronym_dict[agroclimatic_zone]}\"\n"," )\n","\n"," # reset counters\n"," result_num += 1\n"," row_counter = 0\n"," drive_path = f\"{path}/result_{result_num}.csv\"\n"," first_write = True # new file needs header\n","\n"," # Flush leftover rows at end of loop\n"," if row_counter > 0:\n"," print(f'Saving result_{result_num}.csv with {row_counter:,} rows')\n"," upload_file_to_gcs(\n"," drive_path,\n"," f\"ch_{year}_result_{result_num}_{agroclimaticZone_acronym_dict[agroclimatic_zone]}\"\n"," )\n"," export_to_gee(\n"," f\"ch_{year}_result_{result_num}_{agroclimaticZone_acronym_dict[agroclimatic_zone]}\"\n"," )\n"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"SKueMXZDj1NP"},"outputs":[],"source":["# for year in years:\n","# print(year)\n","# df_con = pd.DataFrame()\n","# df_len = 0\n","# result_num = 0\n","# dst_num = 0\n","# for district in dist_list:\n","# print(dst_num, district)\n","# dst_num += 1\n","# try:\n","# df = pd.read_csv(f'drive/MyDrive/TreeHealth/{agroclimatic_zone}/{district}/{year}/result_chm.csv')\n","# except:\n","# continue\n","# df_len += len(df)\n","# df_con = pd.concat([df_con, df])\n","# del(df)\n","# print(df_len)\n","\n","# if df_len > 80000000:\n","# print(f'Saving result_{result_num}.csv')\n","\n","# conditions = [\n","# (df_con['rh50_class'] == 0) & (df_con['rh75_class'] == 0) & (df_con['rh98_class'] == 0),\n","# (df_con['rh50_class'] == 0) & (df_con['rh75_class'] == 0) & (df_con['rh98_class'] == 1),\n","# (df_con['rh50_class'] == 0) & (df_con['rh75_class'] == 1) & (df_con['rh98_class'] == 0),\n","# (df_con['rh50_class'] == 0) & (df_con['rh75_class'] == 1) & (df_con['rh98_class'] == 1),\n","# (df_con['rh50_class'] == 1) & (df_con['rh75_class'] == 0) & (df_con['rh98_class'] == 0),\n","# (df_con['rh50_class'] == 1) & (df_con['rh75_class'] == 0) & (df_con['rh98_class'] == 1)\n","# ]\n","\n","# choices = [0, 0, 1, 2, 1, 2]\n","\n","# df_con['ch_class'] = np.select(conditions, choices, default=3)\n","\n","# path = f'drive/MyDrive/TreeHealth/{agroclimatic_zone}/Compiled Results CH/{year}/'\n","# if not os.path.exists(path):\n","# os.makedirs(path)\n","# drive_path = f'{path}/result_{result_num}.csv'\n","# df_con.to_csv(drive_path, index=False)\n","\n","# upload_file_to_gcs(drive_path, f\"ch_{year}_result_{result_num}_{agroclimaticZone_acronym_dict[agroclimatic_zone]}\")\n","# export_to_gee(f\"ch_{year}_result_{result_num}_{agroclimaticZone_acronym_dict[agroclimatic_zone]}\")\n","\n","# result_num += 1\n","# del(df_con)\n","# df_len = 0\n","# df_con = pd.DataFrame()\n","\n","# if (len(df_con) > 0):\n","# print(f'Saving result_{result_num}.csv')\n","\n","# conditions = [\n","# (df_con['rh50_class'] == 0) & (df_con['rh75_class'] == 0) & (df_con['rh98_class'] == 0),\n","# (df_con['rh50_class'] == 0) & (df_con['rh75_class'] == 0) & (df_con['rh98_class'] == 1),\n","# (df_con['rh50_class'] == 0) & (df_con['rh75_class'] == 1) & (df_con['rh98_class'] == 0),\n","# (df_con['rh50_class'] == 0) & (df_con['rh75_class'] == 1) & (df_con['rh98_class'] == 1),\n","# (df_con['rh50_class'] == 1) & (df_con['rh75_class'] == 0) & (df_con['rh98_class'] == 0),\n","# (df_con['rh50_class'] == 1) & (df_con['rh75_class'] == 0) & (df_con['rh98_class'] == 1)\n","# ]\n","\n","# choices = [0, 0, 1, 2, 1, 2]\n","\n","# df_con['ch_class'] = np.select(conditions, choices, default=3)\n","\n","# path = f'drive/MyDrive/TreeHealth/{agroclimatic_zone}/Compiled Results CH/{year}/'\n","# if not os.path.exists(path):\n","# os.makedirs(path)\n","\n","# drive_path = f'{path}/result_{result_num}.csv'\n","# df_con.to_csv(drive_path, index=False)\n","\n","# upload_file_to_gcs(drive_path, f\"ch_{year}_result_{result_num}_{agroclimaticZone_acronym_dict[agroclimatic_zone]}\")\n","# export_to_gee(f\"ch_{year}_result_{result_num}_{agroclimaticZone_acronym_dict[agroclimatic_zone]}\")\n","\n","# result_num += 1\n","# del(df_con)\n","# df_len = 0\n","# df_con = pd.DataFrame()"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"xl-Lt3vZRfsq"},"outputs":[],"source":["\n","# year = '2023'\n","# path = f'drive/MyDrive/TreeHealth/{agroclimatic_zone}/Compiled Results CH/{year}/'\n","# for result_num in range(6):\n","# drive_path = f\"{path}/result_{result_num}.csv\"\n","# # upload + export\n","# upload_file_to_gcs(\n","# drive_path,\n","# f\"ch_{year}_result_{result_num}_{agroclimaticZone_acronym_dict[agroclimatic_zone]}\"\n","# )\n","# export_to_gee(\n","# f\"ch_{year}_result_{result_num}_{agroclimaticZone_acronym_dict[agroclimatic_zone]}\"\n","# )"]}],"metadata":{"colab":{"collapsed_sections":["_e3eXwraczpB"],"provenance":[{"file_id":"11PyrzNVnNfHa7YM1M37Eg-Ov3L91sSrM","timestamp":1758134547309}]},"kernelspec":{"display_name":"Python 3","name":"python3"},"language_info":{"name":"python"}},"nbformat":4,"nbformat_minor":0} \ No newline at end of file diff --git a/utilities/scripts/tree_health/colab_notebooks/uploadAssets_correction.ipynb b/utilities/scripts/tree_health/colab_notebooks/uploadAssets_correction.ipynb new file mode 100644 index 00000000..b7a02243 --- /dev/null +++ b/utilities/scripts/tree_health/colab_notebooks/uploadAssets_correction.ipynb @@ -0,0 +1 @@ +{"cells":[{"cell_type":"code","execution_count":1,"metadata":{"executionInfo":{"elapsed":10466,"status":"ok","timestamp":1775209278781,"user":{"displayName":"CoRE Stack","userId":"15795073488475210764"},"user_tz":-330},"id":"yewisZzO_XBA"},"outputs":[],"source":["import ee\n","import pandas as pd\n","import json\n","from glob import glob\n","import geemap\n","import pyproj\n","from geopandas import geopandas as gpd\n","from shapely.geometry import Point\n","from copy import deepcopy\n","import numpy as np\n","import os\n","import ast\n","from google.cloud import storage"]},{"cell_type":"code","execution_count":2,"metadata":{"colab":{"base_uri":"https://localhost:8080/","height":35},"executionInfo":{"elapsed":21238,"status":"ok","timestamp":1775209302013,"user":{"displayName":"CoRE Stack","userId":"15795073488475210764"},"user_tz":-330},"id":"XOwz3mnBYzzf","outputId":"1557f773-62e2-4e8e-a6be-a6c619c3f2b6"},"outputs":[{"output_type":"display_data","data":{"text/plain":[""],"text/html":["\n"," \n"," "]},"metadata":{}},{"output_type":"stream","name":"stdout","text":["Mounted at /content/drive\n"]}],"source":["from google.colab import drive\n","drive.mount('/content/drive')"]},{"cell_type":"code","execution_count":8,"metadata":{"colab":{"base_uri":"https://localhost:8080/","height":17},"executionInfo":{"elapsed":583,"status":"ok","timestamp":1775209354321,"user":{"displayName":"CoRE Stack","userId":"15795073488475210764"},"user_tz":-330},"id":"BYyrrpjo0be9","outputId":"161c88fb-cbea-40cd-d20b-09930cf31c6d"},"outputs":[{"output_type":"display_data","data":{"text/plain":[""],"text/html":["\n"," \n"," "]},"metadata":{}}],"source":["ee.Authenticate()\n","ee_project = \"corestack1-dev-alpha\"\n","ee.Initialize(project=\"core-stack-dev-2\")#\"corestack1-dev-alpha\")"]},{"cell_type":"code","execution_count":4,"metadata":{"colab":{"base_uri":"https://localhost:8080/","height":17},"executionInfo":{"elapsed":197,"status":"ok","timestamp":1775209320541,"user":{"displayName":"CoRE Stack","userId":"15795073488475210764"},"user_tz":-330},"id":"xSSojnkB0Twj","outputId":"d81f8d77-f546-4e6e-c895-c55513dc4822"},"outputs":[{"output_type":"display_data","data":{"text/plain":[""],"text/html":["\n"," \n"," "]},"metadata":{}}],"source":["client = storage.Client()\n","bucket = client.get_bucket('core_stack')"]},{"cell_type":"code","execution_count":5,"metadata":{"colab":{"base_uri":"https://localhost:8080/","height":17},"executionInfo":{"elapsed":9,"status":"ok","timestamp":1775209320551,"user":{"displayName":"CoRE Stack","userId":"15795073488475210764"},"user_tz":-330},"id":"RT2Qqbs-Y4Qv","outputId":"cc559b70-3004-4507-a360-7dba8797c768"},"outputs":[{"output_type":"display_data","data":{"text/plain":[""],"text/html":["\n"," \n"," "]},"metadata":{}}],"source":["best_month_dict = {'Eastern Plateau & Hills Region': 'cc_12',\n"," 'Middle Gangetic Plain Region': 'cc_10',\n"," 'Lower Gangetic Plain Region': 'cc_9',\n"," 'Western Himalayan Region': 'cc_8',\n"," 'Eastern Himalayan Region': 'cc_10',\n"," 'Upper Gangetic Plain Region': 'cc_9',\n"," 'Trans Gangetic Plain Region': 'cc_9',\n"," 'Central Plateau & Hills Region': 'cc_7',\n"," 'Western Plateau and Hills Region': 'cc_11',\n"," 'Southern Plateau and Hills Region': 'cc_8',\n"," 'East Coast Plains & Hills Region': 'cc_12'}"]},{"cell_type":"code","execution_count":6,"metadata":{"colab":{"base_uri":"https://localhost:8080/","height":17},"executionInfo":{"elapsed":9,"status":"ok","timestamp":1775209321213,"user":{"displayName":"CoRE Stack","userId":"15795073488475210764"},"user_tz":-330},"id":"OySilp5oft-j","outputId":"ed9bffbe-3b93-4cdd-fb33-62526c03a36c"},"outputs":[{"output_type":"display_data","data":{"text/plain":[""],"text/html":["\n"," \n"," "]},"metadata":{}}],"source":["agroclimaticZone_acronym_dict = {'Eastern Plateau & Hills Region': 'EPAHR',\n"," 'Southern Plateau and Hills Region': 'SPAHR',\n"," 'East Coast Plains & Hills Region': 'ECPHR',\n"," 'Western Plateau and Hills Region': 'WPAHR',\n"," 'Central Plateau & Hills Region': 'CPAHR',\n"," 'Lower Gangetic Plain Region': 'LGPR',\n"," 'Middle Gangetic Plain Region': 'MGPR',\n"," 'Eastern Himalayan Region': 'EHR',\n"," 'Western Himalayan Region': 'WHR',\n"," 'Upper Gangetic Plain Region': 'UGPR',\n"," 'Trans Gangetic Plain Region': 'TGPR',\n"," 'West Coast Plains & Ghat Region': 'WCPGR',\n"," 'Gujarat Plains & Hills Region': 'GPHR',\n"," 'Western Dry Region': 'WDR'}"]},{"cell_type":"code","execution_count":7,"metadata":{"colab":{"base_uri":"https://localhost:8080/","height":17},"executionInfo":{"elapsed":11,"status":"ok","timestamp":1775209323514,"user":{"displayName":"CoRE Stack","userId":"15795073488475210764"},"user_tz":-330},"id":"giCoCm_0fylu","outputId":"782b295f-85ce-497f-8cf7-5fa4bd02ae7d"},"outputs":[{"output_type":"display_data","data":{"text/plain":[""],"text/html":["\n"," \n"," "]},"metadata":{}}],"source":["def upload_file_to_gcs(local_file_path, file_name):\n"," blob = bucket.blob(f'nrm_tree_health/correction_compiled_results/{file_name}.csv') # GCS path\n"," blob.upload_from_filename(local_file_path)\n","\n"," print(\"Upload complete.\")\n","\n","def export_to_gee(file_name):\n"," # CSV GCS path\n"," gcs_path = f'gs://core_stack/nrm_tree_health/correction_compiled_results/{file_name}.csv'\n","\n"," # Create task ID and run table ingestion\n"," task_id = ee.data.newTaskId()[0]\n"," asset_id = f'projects/{ee_project}/assets/tree_characteristics/{file_name}'\n","\n"," manifest = {\n"," 'id': asset_id,\n"," 'sources': [\n"," {\n"," 'primaryPath': gcs_path,\n"," 'additionalPaths': []\n"," }\n"," ]\n"," }\n","\n"," ee.data.startTableIngestion(task_id, manifest)\n"," print(\"Ingestion task started:\", task_id)"]},{"cell_type":"markdown","metadata":{"id":"YGk1NbQIY_9w"},"source":["Merge Correction CSVs"]},{"cell_type":"code","execution_count":9,"metadata":{"colab":{"base_uri":"https://localhost:8080/","height":17},"executionInfo":{"elapsed":43,"status":"ok","timestamp":1775209358495,"user":{"displayName":"CoRE Stack","userId":"15795073488475210764"},"user_tz":-330},"id":"qlIS9q3xZAzR","outputId":"724f3d95-d00a-4f55-c744-97b764af7bd2"},"outputs":[{"output_type":"display_data","data":{"text/plain":[""],"text/html":["\n"," \n"," "]},"metadata":{}}],"source":["acz_list = [\n"," # 'Western Himalayan Region',\n"," 'Eastern Himalayan Region',\n"," # 'Lower Gangetic Plain Region',\n"," # 'Middle Gangetic Plain Region',\n"," # 'Upper Gangetic Plain Region',\n"," # 'Trans Gangetic Plain Region',\n"," # 'Eastern Plateau & Hills Region',\n"," # 'Central Plateau & Hills Region',\n"," # 'Western Plateau and Hills Region',\n"," # 'Southern Plateau and Hills Region',\n"," # 'East Coast Plains & Hills Region'\n"," ]\n","\n"]},{"cell_type":"code","execution_count":10,"metadata":{"colab":{"base_uri":"https://localhost:8080/","height":17},"executionInfo":{"elapsed":10,"status":"ok","timestamp":1775209362961,"user":{"displayName":"CoRE Stack","userId":"15795073488475210764"},"user_tz":-330},"id":"ae3Qg-PHZFWP","outputId":"2a0f4a86-0f2d-4fe3-f889-ed971bfdf4d4"},"outputs":[{"output_type":"display_data","data":{"text/plain":[""],"text/html":["\n"," \n"," "]},"metadata":{}}],"source":["years = ['2017', '2018', '2019', '2020', '2021', '2022', '2023']"]},{"cell_type":"markdown","metadata":{"id":"Ah69USIF_hSV"},"source":["# CCD"]},{"cell_type":"code","execution_count":null,"metadata":{"colab":{"base_uri":"https://localhost:8080/","height":363},"collapsed":true,"id":"Gi4Bw7-LZJHw","outputId":"3af2dd77-cd05-4aaa-a89c-0bd7ffb5568a"},"outputs":[{"output_type":"display_data","data":{"text/plain":[""],"text/html":["\n"," \n"," "]},"metadata":{}},{"output_type":"stream","name":"stdout","text":["Eastern Plateau & Hills Region\n","2020\n","length(dist_list): 119\n","0 East Godavari\n","1 Srikakulam\n","2 Visakhapatnam\n","3 Vizianagaram\n","4 AurangabadB\n","5 Banka\n","6 Gaya\n","7 Jamui\n","8 Nawada\n","9 Rohtas\n","10 Baloda Bazar\n","11 Balod\n","12 BalrampurC\n","13 Bastar\n","14 Bemetara\n","15 BijapurC\n"]}],"source":["# CCD\n","\n","for agroclimatic_zone in acz_list:\n"," print(agroclimatic_zone)\n"," for year in years:\n"," print(year)\n"," df = pd.read_csv(f'drive/MyDrive/TreeHealth/Agroclimatic_regions/{agroclimatic_zone}.csv')\n"," dist_list = list(df['Name'])\n"," print(f'length(dist_list): {len(dist_list)}')\n","\n"," i = 0\n"," merged_df = pd.DataFrame()\n"," for district in dist_list:\n"," print(i, district)\n"," i += 1\n","\n"," try:\n"," df = pd.read_csv(f'drive/MyDrive/TreeHealth/{agroclimatic_zone}/{district}/{year}/result_monthly_cc_corrections.csv')\n"," except:\n"," continue\n","\n"," merged_df = pd.concat([merged_df, df], ignore_index=True)\n","\n"," print(f'length(merged_df): {len(merged_df)}')\n","\n"," cols = merged_df.columns\n"," for i in range(1, len(cols)):\n"," merged_df[cols[i]] = merged_df[cols[i]].astype('Int64')\n"," drive_path = f'drive/MyDrive/TreeHealth/{agroclimatic_zone}/corrections_{year}.csv'\n"," merged_df.to_csv(drive_path, index=False)\n","\n"," file_name = f\"corrections_ccd_{year}_result_0_{agroclimaticZone_acronym_dict[agroclimatic_zone]}\"\n"," upload_file_to_gcs(drive_path, file_name)\n"," export_to_gee(file_name)"]},{"cell_type":"markdown","metadata":{"id":"J7kg35BL_rWU"},"source":["# CH"]},{"cell_type":"code","execution_count":11,"metadata":{"colab":{"base_uri":"https://localhost:8080/","height":1000},"id":"umuwxquJLI1q","executionInfo":{"status":"ok","timestamp":1775217779330,"user_tz":-330,"elapsed":8414523,"user":{"displayName":"CoRE Stack","userId":"15795073488475210764"}},"outputId":"0605ef8a-3f71-4edc-a097-442968229a59"},"outputs":[{"output_type":"display_data","data":{"text/plain":[""],"text/html":["\n"," \n"," "]},"metadata":{}},{"output_type":"stream","name":"stdout","text":["Eastern Himalayan Region\n","2017\n","length(dist_list): 99\n","0 Anjaw\n","length(merged_df): 2979532\n","1 Changlang\n","length(merged_df): 3307447\n","2 Dibang Valley\n","length(merged_df): 4194324\n","3 East Kameng\n","length(merged_df): 4352964\n","4 East Siang\n","length(merged_df): 4458119\n","5 Kurung Kumey\n","6 Lohit\n","length(merged_df): 5274731\n","7 Longding\n","length(merged_df): 5300151\n","8 Lower Dibang Valley\n","length(merged_df): 6630346\n","9 Lower Subansiri\n","10 Namsai\n","length(merged_df): 6654526\n","11 Papum Pare\n","12 Tawang\n","13 Tirap\n","length(merged_df): 6808687\n","14 Upper Siang\n","15 Upper Subansiri\n","16 West Kameng\n","length(merged_df): 7216607\n","17 West Siang\n","18 Baksa\n","length(merged_df): 7300480\n","19 Barpeta\n","length(merged_df): 7319087\n","20 Bongaigaon\n","length(merged_df): 7335411\n","21 Cachar\n","length(merged_df): 7372150\n","22 Chirang\n","length(merged_df): 7420786\n","23 Darrang\n","length(merged_df): 7454804\n","24 Dhemaji\n","length(merged_df): 7457716\n","25 Dhubri\n","length(merged_df): 7488291\n","26 Dibrugarh\n","length(merged_df): 7520020\n","27 Dima Hasao\n","length(merged_df): 7968962\n","28 Goalpara\n","length(merged_df): 7993104\n","29 Golaghat\n","length(merged_df): 8007504\n","30 Hailakandi\n","length(merged_df): 8013767\n","31 Jorhat\n","length(merged_df): 8023171\n","32 Kamrup Metropolitan\n","length(merged_df): 8124708\n","33 Kamrup\n","length(merged_df): 8236975\n","34 Karbi Anglong\n","length(merged_df): 10643741\n","35 Karimganj\n","length(merged_df): 10760499\n","36 Kokrajhar\n","length(merged_df): 10925086\n","37 Lakhimpur\n","length(merged_df): 10933650\n","38 Morigaon\n","length(merged_df): 11075910\n","39 Nagaon\n","length(merged_df): 11178452\n","40 Nalbari\n","length(merged_df): 11196382\n","41 Sivasagar\n","length(merged_df): 11234982\n","42 Sonitpur\n","length(merged_df): 11367250\n","43 Tinsukia\n","length(merged_df): 11619699\n","44 Udalguri\n","length(merged_df): 11682623\n","45 Bishnupur\n","46 Chandel\n","47 Churachandpur\n","48 Imphal East\n","49 Imphal West\n","50 Senapati\n","length(merged_df): 12040830\n","51 Tamenglong\n","length(merged_df): 12293929\n","52 Thoubal\n","53 Ukhrul\n","length(merged_df): 12351297\n","54 East Garo Hills\n","length(merged_df): 12481308\n","55 East Khasi Hills\n","length(merged_df): 13191302\n","56 Jaintia Hills\n","length(merged_df): 14527081\n","57 North Garo Hills\n","length(merged_df): 14543576\n","58 Ri Bhoi\n","length(merged_df): 15479001\n","59 South Garo Hills\n","length(merged_df): 15711222\n","60 South West Garo Hills\n","length(merged_df): 15721187\n","61 South West Khasi Hills\n","length(merged_df): 15973372\n","62 West Garo Hills\n","length(merged_df): 16109333\n","63 West Khasi Hills\n","length(merged_df): 16882649\n","64 Aizawl\n","length(merged_df): 16883641\n","65 Champhai\n","length(merged_df): 16962300\n","66 Kolasib\n","67 Lawangtlai\n","length(merged_df): 17607846\n","68 Lunglei\n","length(merged_df): 17777978\n","69 Mamit\n","length(merged_df): 17816098\n","70 Saiha\n","length(merged_df): 18337927\n","71 Serchhip\n","length(merged_df): 18456898\n","72 Dimapur\n","length(merged_df): 18653296\n","73 Kiphire\n","length(merged_df): 18828710\n","74 Kohima\n","length(merged_df): 19026827\n","75 Longleng\n","76 Mokokchung\n","77 Mon\n","length(merged_df): 19026889\n","78 Peren\n","length(merged_df): 19635289\n","79 Phek\n","length(merged_df): 19663020\n","80 Tuensang\n","length(merged_df): 19889386\n","81 Wokha\n","length(merged_df): 20040494\n","82 Zunheboto\n","83 East Sikkim\n","84 North Sikkim\n","85 South Sikkim\n","length(merged_df): 20042784\n","86 West Sikkim\n","length(merged_df): 20042906\n","87 Dhalai\n","length(merged_df): 20349925\n","88 Gomati\n","length(merged_df): 20559679\n","89 Khowai\n","length(merged_df): 20770547\n","90 North Tripura\n","length(merged_df): 20915052\n","91 Sipahijala\n","length(merged_df): 21005581\n","92 South Tripura\n","length(merged_df): 21023307\n","93 Unokoti\n","length(merged_df): 21069492\n","94 West Tripura\n","length(merged_df): 21162616\n","95 Alipurduar\n","length(merged_df): 21333417\n","96 Darjiling\n","length(merged_df): 21731155\n","97 Jalpaiguri\n","length(merged_df): 21848690\n","98 Koch Bihar\n","length(merged_df): 21905819\n","Saving merged_corrections_0.csv\n","Upload complete.\n","Ingestion task started: a50d3798-10da-4004-a2fb-6e0e6aa129c2\n","2018\n","length(dist_list): 99\n","0 Anjaw\n","length(merged_df): 478344\n","1 Changlang\n","length(merged_df): 2444511\n","2 Dibang Valley\n","length(merged_df): 3836445\n","3 East Kameng\n","length(merged_df): 5122433\n","4 East Siang\n","length(merged_df): 5540789\n","5 Kurung Kumey\n","length(merged_df): 6210350\n","6 Lohit\n","length(merged_df): 7154987\n","7 Longding\n","length(merged_df): 7518784\n","8 Lower Dibang Valley\n","length(merged_df): 9181092\n","9 Lower Subansiri\n","length(merged_df): 9544762\n","10 Namsai\n","length(merged_df): 9599147\n","11 Papum Pare\n","length(merged_df): 10071058\n","12 Tawang\n","13 Tirap\n","length(merged_df): 10651507\n","14 Upper Siang\n","15 Upper Subansiri\n","length(merged_df): 10996049\n","16 West Kameng\n","length(merged_df): 11911902\n","17 West Siang\n","length(merged_df): 12567368\n","18 Baksa\n","length(merged_df): 12741540\n","19 Barpeta\n","length(merged_df): 12814699\n","20 Bongaigaon\n","length(merged_df): 12865104\n","21 Cachar\n","length(merged_df): 13634666\n","22 Chirang\n","length(merged_df): 13737843\n","23 Darrang\n","length(merged_df): 13803079\n","24 Dhemaji\n","length(merged_df): 14005416\n","25 Dhubri\n","length(merged_df): 14086546\n","26 Dibrugarh\n","length(merged_df): 14327414\n","27 Dima Hasao\n","length(merged_df): 15403060\n","28 Goalpara\n","length(merged_df): 15456008\n","29 Golaghat\n","length(merged_df): 15577333\n","30 Hailakandi\n","length(merged_df): 15668824\n","31 Jorhat\n","length(merged_df): 15803737\n","32 Kamrup Metropolitan\n","length(merged_df): 15886696\n","33 Kamrup\n","length(merged_df): 16170410\n","34 Karbi Anglong\n","length(merged_df): 17867132\n","35 Karimganj\n","length(merged_df): 18041501\n","36 Kokrajhar\n","length(merged_df): 18451796\n","37 Lakhimpur\n","length(merged_df): 18617844\n","38 Morigaon\n","length(merged_df): 18681752\n","39 Nagaon\n","length(merged_df): 18889057\n","40 Nalbari\n","length(merged_df): 18936490\n","41 Sivasagar\n","length(merged_df): 19070646\n","42 Sonitpur\n","length(merged_df): 19487051\n","43 Tinsukia\n","length(merged_df): 20132264\n","44 Udalguri\n","length(merged_df): 20234159\n","45 Bishnupur\n","length(merged_df): 20256633\n","46 Chandel\n","length(merged_df): 21456762\n","47 Churachandpur\n","length(merged_df): 22912733\n","48 Imphal East\n","length(merged_df): 22977450\n","49 Imphal West\n","length(merged_df): 23010433\n","50 Senapati\n","length(merged_df): 24073080\n","51 Tamenglong\n","length(merged_df): 25665819\n","52 Thoubal\n","length(merged_df): 25718095\n","53 Ukhrul\n","length(merged_df): 27128826\n","54 East Garo Hills\n","length(merged_df): 27513780\n","55 East Khasi Hills\n","length(merged_df): 28529934\n","56 Jaintia Hills\n","length(merged_df): 29485284\n","57 North Garo Hills\n","length(merged_df): 29544664\n","58 Ri Bhoi\n","length(merged_df): 30143241\n","59 South Garo Hills\n","length(merged_df): 30579916\n","60 South West Garo Hills\n","length(merged_df): 30602928\n","61 South West Khasi Hills\n","length(merged_df): 31055221\n","62 West Garo Hills\n","length(merged_df): 31366797\n","63 West Khasi Hills\n","length(merged_df): 32639956\n","64 Aizawl\n","length(merged_df): 33683017\n","65 Champhai\n","length(merged_df): 34876756\n","66 Kolasib\n","length(merged_df): 35235014\n","67 Lawangtlai\n","length(merged_df): 36017069\n","68 Lunglei\n","length(merged_df): 37422177\n","69 Mamit\n","length(merged_df): 38303422\n","70 Saiha\n","length(merged_df): 39353050\n","71 Serchhip\n","length(merged_df): 39818787\n","72 Dimapur\n","length(merged_df): 39983662\n","73 Kiphire\n","length(merged_df): 40462734\n","74 Kohima\n","length(merged_df): 40960637\n","75 Longleng\n","length(merged_df): 41132686\n","76 Mokokchung\n","length(merged_df): 41676302\n","77 Mon\n","length(merged_df): 42316638\n","78 Peren\n","length(merged_df): 43022268\n","79 Phek\n","length(merged_df): 43711636\n","80 Tuensang\n","length(merged_df): 44510144\n","81 Wokha\n","length(merged_df): 45009024\n","82 Zunheboto\n","length(merged_df): 45589344\n","83 East Sikkim\n","84 North Sikkim\n","85 South Sikkim\n","length(merged_df): 45594992\n","86 West Sikkim\n","length(merged_df): 45595116\n","87 Dhalai\n","length(merged_df): 46143129\n","88 Gomati\n","length(merged_df): 46453951\n","89 Khowai\n","length(merged_df): 46721002\n","90 North Tripura\n","length(merged_df): 47019475\n","91 Sipahijala\n","length(merged_df): 47148693\n","92 South Tripura\n","length(merged_df): 47417704\n","93 Unokoti\n","length(merged_df): 47531409\n","94 West Tripura\n","length(merged_df): 47655435\n","95 Alipurduar\n","length(merged_df): 48182964\n","96 Darjiling\n","length(merged_df): 48864414\n","97 Jalpaiguri\n","length(merged_df): 49084292\n","98 Koch Bihar\n","length(merged_df): 49297786\n","Saving merged_corrections_0.csv\n","Upload complete.\n","Ingestion task started: ba30440b-954b-43bb-814b-f9240309b01a\n","2019\n","length(dist_list): 99\n","0 Anjaw\n","length(merged_df): 478344\n","1 Changlang\n","length(merged_df): 2444511\n","2 Dibang Valley\n","length(merged_df): 3836445\n","3 East Kameng\n","length(merged_df): 5122433\n","4 East Siang\n","length(merged_df): 5540789\n","5 Kurung Kumey\n","length(merged_df): 6210350\n","6 Lohit\n","length(merged_df): 7154987\n","7 Longding\n","length(merged_df): 7518784\n","8 Lower Dibang Valley\n","length(merged_df): 9181092\n","9 Lower Subansiri\n","length(merged_df): 9544762\n","10 Namsai\n","length(merged_df): 9599147\n","11 Papum Pare\n","length(merged_df): 10071058\n","12 Tawang\n","13 Tirap\n","length(merged_df): 10651507\n","14 Upper Siang\n","15 Upper Subansiri\n","length(merged_df): 10996049\n","16 West Kameng\n","length(merged_df): 11911902\n","17 West Siang\n","length(merged_df): 12567368\n","18 Baksa\n","length(merged_df): 12741540\n","19 Barpeta\n","length(merged_df): 12814699\n","20 Bongaigaon\n","length(merged_df): 12865104\n","21 Cachar\n","length(merged_df): 13634666\n","22 Chirang\n","length(merged_df): 13737843\n","23 Darrang\n","length(merged_df): 13803079\n","24 Dhemaji\n","length(merged_df): 14005416\n","25 Dhubri\n","length(merged_df): 14086546\n","26 Dibrugarh\n","length(merged_df): 14327414\n","27 Dima Hasao\n","length(merged_df): 15403060\n","28 Goalpara\n","length(merged_df): 15456008\n","29 Golaghat\n","length(merged_df): 15577333\n","30 Hailakandi\n","length(merged_df): 15668824\n","31 Jorhat\n","length(merged_df): 15803737\n","32 Kamrup Metropolitan\n","length(merged_df): 15886696\n","33 Kamrup\n","length(merged_df): 16170410\n","34 Karbi Anglong\n","length(merged_df): 17867132\n","35 Karimganj\n","length(merged_df): 18041501\n","36 Kokrajhar\n","length(merged_df): 18451796\n","37 Lakhimpur\n","length(merged_df): 18617844\n","38 Morigaon\n","length(merged_df): 18681752\n","39 Nagaon\n","length(merged_df): 18889057\n","40 Nalbari\n","length(merged_df): 18936490\n","41 Sivasagar\n","length(merged_df): 19070646\n","42 Sonitpur\n","length(merged_df): 19487051\n","43 Tinsukia\n","length(merged_df): 20132264\n","44 Udalguri\n","length(merged_df): 20234159\n","45 Bishnupur\n","length(merged_df): 20256633\n","46 Chandel\n","length(merged_df): 21456762\n","47 Churachandpur\n","length(merged_df): 22912733\n","48 Imphal East\n","length(merged_df): 22977450\n","49 Imphal West\n","length(merged_df): 23010433\n","50 Senapati\n","length(merged_df): 24073080\n","51 Tamenglong\n","length(merged_df): 25665819\n","52 Thoubal\n","length(merged_df): 25718095\n","53 Ukhrul\n","length(merged_df): 27128826\n","54 East Garo Hills\n","length(merged_df): 27513780\n","55 East Khasi Hills\n","length(merged_df): 28529934\n","56 Jaintia Hills\n","length(merged_df): 29485284\n","57 North Garo Hills\n","length(merged_df): 29544664\n","58 Ri Bhoi\n","length(merged_df): 30143241\n","59 South Garo Hills\n","length(merged_df): 30579916\n","60 South West Garo Hills\n","length(merged_df): 30602928\n","61 South West Khasi Hills\n","length(merged_df): 31055221\n","62 West Garo Hills\n","length(merged_df): 31366797\n","63 West Khasi Hills\n","length(merged_df): 32639956\n","64 Aizawl\n","length(merged_df): 33683017\n","65 Champhai\n","length(merged_df): 34876756\n","66 Kolasib\n","length(merged_df): 35235014\n","67 Lawangtlai\n","length(merged_df): 36017069\n","68 Lunglei\n","length(merged_df): 37422177\n","69 Mamit\n","length(merged_df): 38303422\n","70 Saiha\n","length(merged_df): 39353050\n","71 Serchhip\n","length(merged_df): 39818787\n","72 Dimapur\n","length(merged_df): 39983662\n","73 Kiphire\n","length(merged_df): 40462734\n","74 Kohima\n","length(merged_df): 40960637\n","75 Longleng\n","length(merged_df): 41132686\n","76 Mokokchung\n","length(merged_df): 41676302\n","77 Mon\n","length(merged_df): 42316638\n","78 Peren\n","length(merged_df): 43022268\n","79 Phek\n","length(merged_df): 43711636\n","80 Tuensang\n","length(merged_df): 44510144\n","81 Wokha\n","length(merged_df): 45009024\n","82 Zunheboto\n","length(merged_df): 45589344\n","83 East Sikkim\n","84 North Sikkim\n","85 South Sikkim\n","length(merged_df): 45594992\n","86 West Sikkim\n","length(merged_df): 45595116\n","87 Dhalai\n","length(merged_df): 46143129\n","88 Gomati\n","length(merged_df): 46453951\n","89 Khowai\n","length(merged_df): 46721002\n","90 North Tripura\n","length(merged_df): 47019475\n","91 Sipahijala\n","length(merged_df): 47148693\n","92 South Tripura\n","length(merged_df): 47417704\n","93 Unokoti\n","length(merged_df): 47531409\n","94 West Tripura\n","length(merged_df): 47655435\n","95 Alipurduar\n","length(merged_df): 48182964\n","96 Darjiling\n","length(merged_df): 49123864\n","97 Jalpaiguri\n","length(merged_df): 49343742\n","98 Koch Bihar\n","length(merged_df): 49557236\n","Saving merged_corrections_0.csv\n","Upload complete.\n","Ingestion task started: 5533b929-ee31-49af-88d3-d3604cacf2fc\n","2020\n","length(dist_list): 99\n","0 Anjaw\n","length(merged_df): 1330356\n","1 Changlang\n","length(merged_df): 3381834\n","2 Dibang Valley\n","length(merged_df): 4485987\n","3 East Kameng\n","length(merged_df): 5946992\n","4 East Siang\n","length(merged_df): 6841619\n","5 Kurung Kumey\n","length(merged_df): 7374955\n","6 Lohit\n","length(merged_df): 8623493\n","7 Longding\n","length(merged_df): 9122254\n","8 Lower Dibang Valley\n","length(merged_df): 10832333\n","9 Lower Subansiri\n","length(merged_df): 11508961\n","10 Namsai\n","length(merged_df): 11572983\n","11 Papum Pare\n","length(merged_df): 11964446\n","12 Tawang\n","13 Tirap\n","length(merged_df): 12482533\n","14 Upper Siang\n","length(merged_df): 12986325\n","15 Upper Subansiri\n","length(merged_df): 13569984\n","16 West Kameng\n","length(merged_df): 14629622\n","17 West Siang\n","length(merged_df): 15473209\n","18 Baksa\n","length(merged_df): 15724686\n","19 Barpeta\n","length(merged_df): 15796660\n","20 Bongaigaon\n","length(merged_df): 15864834\n","21 Cachar\n","length(merged_df): 16954573\n","22 Chirang\n","length(merged_df): 17208861\n","23 Darrang\n","length(merged_df): 17261514\n","24 Dhemaji\n","length(merged_df): 17476816\n","25 Dhubri\n","length(merged_df): 17557170\n","26 Dibrugarh\n","length(merged_df): 17762589\n","27 Dima Hasao\n","length(merged_df): 19560412\n","28 Goalpara\n","length(merged_df): 19608366\n","29 Golaghat\n","length(merged_df): 19763513\n","30 Hailakandi\n","length(merged_df): 19944247\n","31 Jorhat\n","length(merged_df): 20226737\n","32 Kamrup Metropolitan\n","length(merged_df): 20346460\n","33 Kamrup\n","length(merged_df): 20894960\n","34 Karbi Anglong\n","length(merged_df): 24409800\n","35 Karimganj\n","length(merged_df): 24720227\n","36 Kokrajhar\n","length(merged_df): 25249349\n","37 Lakhimpur\n","length(merged_df): 25394037\n","38 Morigaon\n","length(merged_df): 25464656\n","39 Nagaon\n","length(merged_df): 25730146\n","40 Nalbari\n","length(merged_df): 25783561\n","41 Sivasagar\n","length(merged_df): 25989411\n","42 Sonitpur\n","length(merged_df): 26322156\n","43 Tinsukia\n","length(merged_df): 26849930\n","44 Udalguri\n","length(merged_df): 26974580\n","45 Bishnupur\n","length(merged_df): 27012133\n","46 Chandel\n","length(merged_df): 29576779\n","47 Churachandpur\n","length(merged_df): 34538630\n","48 Imphal East\n","length(merged_df): 34666561\n","49 Imphal West\n","length(merged_df): 34724393\n","50 Senapati\n","length(merged_df): 36660245\n","51 Tamenglong\n","length(merged_df): 39328598\n","52 Thoubal\n","length(merged_df): 39442977\n","53 Ukhrul\n","length(merged_df): 42235241\n","54 East Garo Hills\n","length(merged_df): 42628773\n","55 East Khasi Hills\n","length(merged_df): 43646393\n","56 Jaintia Hills\n","length(merged_df): 45028991\n","57 North Garo Hills\n","length(merged_df): 45074050\n","58 Ri Bhoi\n","length(merged_df): 45875336\n","59 South Garo Hills\n","length(merged_df): 46412583\n","60 South West Garo Hills\n","length(merged_df): 46429328\n","61 South West Khasi Hills\n","length(merged_df): 46933540\n","62 West Garo Hills\n","length(merged_df): 47220989\n","63 West Khasi Hills\n","length(merged_df): 48539439\n","64 Aizawl\n","length(merged_df): 50741733\n","Saving merged_corrections_0.csv\n"]},{"output_type":"stream","name":"stderr","text":["WARNING:google_auth_httplib2:httplib2 transport does not support per-request timeout. Set the timeout when constructing the httplib2.Http instance.\n","WARNING:google_auth_httplib2:httplib2 transport does not support per-request timeout. Set the timeout when constructing the httplib2.Http instance.\n"]},{"output_type":"stream","name":"stdout","text":["Upload complete.\n","Ingestion task started: 719ee0b0-b6f9-4f82-a567-e1de6a176ab3\n","65 Champhai\n","length(merged_df): 3634458\n","66 Kolasib\n","length(merged_df): 4210001\n","67 Lawangtlai\n","length(merged_df): 5163272\n","68 Lunglei\n","length(merged_df): 7603186\n","69 Mamit\n","length(merged_df): 8935328\n","70 Saiha\n","length(merged_df): 10161350\n","71 Serchhip\n","length(merged_df): 10989166\n","72 Dimapur\n","length(merged_df): 11231066\n","73 Kiphire\n","length(merged_df): 11796555\n","74 Kohima\n","length(merged_df): 12588621\n","75 Longleng\n","length(merged_df): 12837079\n","76 Mokokchung\n","length(merged_df): 13936156\n","77 Mon\n","length(merged_df): 14914721\n","78 Peren\n","length(merged_df): 15919623\n","79 Phek\n","length(merged_df): 17010418\n","80 Tuensang\n","length(merged_df): 18037322\n","81 Wokha\n","length(merged_df): 19164284\n","82 Zunheboto\n","length(merged_df): 20101345\n","83 East Sikkim\n","84 North Sikkim\n","85 South Sikkim\n","length(merged_df): 20114100\n","86 West Sikkim\n","length(merged_df): 20114504\n","87 Dhalai\n","length(merged_df): 20852781\n","88 Gomati\n","length(merged_df): 21287454\n","89 Khowai\n","length(merged_df): 21705560\n","90 North Tripura\n","length(merged_df): 22057720\n","91 Sipahijala\n","length(merged_df): 22183921\n","92 South Tripura\n","length(merged_df): 22465453\n","93 Unokoti\n","length(merged_df): 22613908\n","94 West Tripura\n","length(merged_df): 22735736\n","95 Alipurduar\n","length(merged_df): 23223854\n","96 Darjiling\n","length(merged_df): 24393328\n","97 Jalpaiguri\n","length(merged_df): 24698003\n","98 Koch Bihar\n","length(merged_df): 24915683\n","Saving merged_corrections_1.csv\n","Upload complete.\n","Ingestion task started: b4ac2c6e-32aa-44a8-a4a6-dbd936a44ca4\n","2021\n","length(dist_list): 99\n","0 Anjaw\n","length(merged_df): 1330356\n","1 Changlang\n","length(merged_df): 3381834\n","2 Dibang Valley\n","length(merged_df): 4485987\n","3 East Kameng\n","length(merged_df): 5946992\n","4 East Siang\n","length(merged_df): 6841619\n","5 Kurung Kumey\n","length(merged_df): 7374955\n","6 Lohit\n","length(merged_df): 8623493\n","7 Longding\n","length(merged_df): 9122254\n","8 Lower Dibang Valley\n","length(merged_df): 10832333\n","9 Lower Subansiri\n","length(merged_df): 11508961\n","10 Namsai\n","length(merged_df): 11572983\n","11 Papum Pare\n","length(merged_df): 11964446\n","12 Tawang\n","13 Tirap\n","length(merged_df): 12482533\n","14 Upper Siang\n","length(merged_df): 12986325\n","15 Upper Subansiri\n","length(merged_df): 13569984\n","16 West Kameng\n","length(merged_df): 14629622\n","17 West Siang\n","length(merged_df): 15473209\n","18 Baksa\n","length(merged_df): 15724686\n","19 Barpeta\n","length(merged_df): 15796660\n","20 Bongaigaon\n","length(merged_df): 15864834\n","21 Cachar\n","length(merged_df): 16954573\n","22 Chirang\n","length(merged_df): 17208861\n","23 Darrang\n","length(merged_df): 17261514\n","24 Dhemaji\n","length(merged_df): 17476816\n","25 Dhubri\n","length(merged_df): 17557170\n","26 Dibrugarh\n","length(merged_df): 17762589\n","27 Dima Hasao\n","length(merged_df): 19560412\n","28 Goalpara\n","length(merged_df): 19608366\n","29 Golaghat\n","length(merged_df): 19763513\n","30 Hailakandi\n","length(merged_df): 19944247\n","31 Jorhat\n","length(merged_df): 20226737\n","32 Kamrup Metropolitan\n","length(merged_df): 20346460\n","33 Kamrup\n","length(merged_df): 20894960\n","34 Karbi Anglong\n","35 Karimganj\n","length(merged_df): 21205387\n","36 Kokrajhar\n","length(merged_df): 21734509\n","37 Lakhimpur\n","length(merged_df): 21879197\n","38 Morigaon\n","length(merged_df): 21949816\n","39 Nagaon\n","length(merged_df): 22215306\n","40 Nalbari\n","length(merged_df): 22268721\n","41 Sivasagar\n","length(merged_df): 22474571\n","42 Sonitpur\n","length(merged_df): 22807316\n","43 Tinsukia\n","length(merged_df): 23335090\n","44 Udalguri\n","length(merged_df): 23459740\n","45 Bishnupur\n","length(merged_df): 23497293\n","46 Chandel\n","length(merged_df): 26061939\n","47 Churachandpur\n","length(merged_df): 31023790\n","48 Imphal East\n","length(merged_df): 31151721\n","49 Imphal West\n","length(merged_df): 31209553\n","50 Senapati\n","length(merged_df): 33145405\n","51 Tamenglong\n","length(merged_df): 35813758\n","52 Thoubal\n","length(merged_df): 35928137\n","53 Ukhrul\n","length(merged_df): 38720401\n","54 East Garo Hills\n","length(merged_df): 39113933\n","55 East Khasi Hills\n","length(merged_df): 40131553\n","56 Jaintia Hills\n","length(merged_df): 41514151\n","57 North Garo Hills\n","length(merged_df): 41559210\n","58 Ri Bhoi\n","length(merged_df): 42360496\n","59 South Garo Hills\n","length(merged_df): 42897743\n","60 South West Garo Hills\n","length(merged_df): 42914488\n","61 South West Khasi Hills\n","length(merged_df): 43418700\n","62 West Garo Hills\n","length(merged_df): 43706149\n","63 West Khasi Hills\n","length(merged_df): 45024599\n","64 Aizawl\n","length(merged_df): 47226893\n","65 Champhai\n","length(merged_df): 50861351\n","Saving merged_corrections_0.csv\n","Upload complete.\n","Ingestion task started: 6d28ee07-5504-47db-bef6-41b776609a75\n","66 Kolasib\n","length(merged_df): 575543\n","67 Lawangtlai\n","length(merged_df): 1528814\n","68 Lunglei\n","length(merged_df): 3968728\n","69 Mamit\n","length(merged_df): 5300870\n","70 Saiha\n","length(merged_df): 6526892\n","71 Serchhip\n","length(merged_df): 7354708\n","72 Dimapur\n","length(merged_df): 7596608\n","73 Kiphire\n","length(merged_df): 8162097\n","74 Kohima\n","length(merged_df): 8954163\n","75 Longleng\n","length(merged_df): 9202621\n","76 Mokokchung\n","length(merged_df): 10301698\n","77 Mon\n","length(merged_df): 11280263\n","78 Peren\n","length(merged_df): 12285165\n","79 Phek\n","length(merged_df): 13375960\n","80 Tuensang\n","length(merged_df): 14402864\n","81 Wokha\n","length(merged_df): 15529826\n","82 Zunheboto\n","length(merged_df): 16466887\n","83 East Sikkim\n","84 North Sikkim\n","85 South Sikkim\n","length(merged_df): 16479642\n","86 West Sikkim\n","length(merged_df): 16480046\n","87 Dhalai\n","length(merged_df): 17218323\n","88 Gomati\n","length(merged_df): 17652996\n","89 Khowai\n","length(merged_df): 18071102\n","90 North Tripura\n","length(merged_df): 18423262\n","91 Sipahijala\n","length(merged_df): 18549463\n","92 South Tripura\n","length(merged_df): 18830995\n","93 Unokoti\n","length(merged_df): 18979450\n","94 West Tripura\n","length(merged_df): 19101278\n","95 Alipurduar\n","length(merged_df): 19589396\n","96 Darjiling\n","length(merged_df): 20711886\n","97 Jalpaiguri\n","length(merged_df): 21016561\n","98 Koch Bihar\n","length(merged_df): 21234241\n","Saving merged_corrections_1.csv\n","Upload complete.\n","Ingestion task started: 07473749-9783-4784-9b75-52ef5c1bd445\n","2022\n","length(dist_list): 99\n","0 Anjaw\n","length(merged_df): 1848049\n","1 Changlang\n","length(merged_df): 4620455\n","2 Dibang Valley\n","length(merged_df): 7067896\n","3 East Kameng\n","length(merged_df): 9238528\n","4 East Siang\n","length(merged_df): 10273774\n","5 Kurung Kumey\n","length(merged_df): 12139221\n","6 Lohit\n","length(merged_df): 13411891\n","7 Longding\n","length(merged_df): 13957060\n","8 Lower Dibang Valley\n","length(merged_df): 15800235\n","9 Lower Subansiri\n","length(merged_df): 17537049\n","10 Namsai\n","length(merged_df): 17592906\n","11 Papum Pare\n","length(merged_df): 19454295\n","12 Tawang\n","length(merged_df): 20259605\n","13 Tirap\n","length(merged_df): 20849587\n","14 Upper Siang\n","length(merged_df): 25013318\n","15 Upper Subansiri\n","length(merged_df): 26200628\n","16 West Kameng\n","length(merged_df): 29142728\n","17 West Siang\n","length(merged_df): 31692865\n","18 Baksa\n","length(merged_df): 31844938\n","19 Barpeta\n","length(merged_df): 31948301\n","20 Bongaigaon\n","length(merged_df): 32020012\n","21 Cachar\n","length(merged_df): 33015996\n","22 Chirang\n","length(merged_df): 33110098\n","23 Darrang\n","length(merged_df): 33184191\n","24 Dhemaji\n","length(merged_df): 33459882\n","25 Dhubri\n","length(merged_df): 33584733\n","26 Dibrugarh\n","length(merged_df): 33854284\n","27 Dima Hasao\n","length(merged_df): 35107231\n","28 Goalpara\n","length(merged_df): 35181002\n","29 Golaghat\n","length(merged_df): 35345503\n","30 Hailakandi\n","length(merged_df): 35574708\n","31 Jorhat\n","length(merged_df): 35772826\n","32 Kamrup Metropolitan\n","length(merged_df): 35930324\n","33 Kamrup\n","length(merged_df): 36346509\n","34 Karbi Anglong\n","length(merged_df): 38769946\n","35 Karimganj\n","length(merged_df): 39093925\n","36 Kokrajhar\n","length(merged_df): 39620058\n","37 Lakhimpur\n","length(merged_df): 39918127\n","38 Morigaon\n","length(merged_df): 40003240\n","39 Nagaon\n","length(merged_df): 40244487\n","40 Nalbari\n","length(merged_df): 40300500\n","41 Sivasagar\n","length(merged_df): 40499034\n","42 Sonitpur\n","length(merged_df): 40866091\n","43 Tinsukia\n","length(merged_df): 41475492\n","44 Udalguri\n","length(merged_df): 41567105\n","45 Bishnupur\n","length(merged_df): 41604433\n","46 Chandel\n","length(merged_df): 42744913\n","47 Churachandpur\n","length(merged_df): 43872322\n","48 Imphal East\n","length(merged_df): 43964012\n","49 Imphal West\n","length(merged_df): 44013228\n","50 Senapati\n","length(merged_df): 45464578\n","51 Tamenglong\n","length(merged_df): 47644037\n","52 Thoubal\n","length(merged_df): 47707540\n","53 Ukhrul\n","length(merged_df): 50022798\n","Saving merged_corrections_0.csv\n","Upload complete.\n","Ingestion task started: b7a98fca-05eb-42b0-85c3-de59164d0658\n","54 East Garo Hills\n","length(merged_df): 357834\n","55 East Khasi Hills\n","length(merged_df): 1398969\n","56 Jaintia Hills\n","length(merged_df): 2608139\n","57 North Garo Hills\n","length(merged_df): 2680692\n","58 Ri Bhoi\n","length(merged_df): 3610989\n","59 South Garo Hills\n","length(merged_df): 4039361\n","60 South West Garo Hills\n","length(merged_df): 4071718\n","61 South West Khasi Hills\n","length(merged_df): 4490566\n","62 West Garo Hills\n","length(merged_df): 4804823\n","63 West Khasi Hills\n","length(merged_df): 6123187\n","64 Aizawl\n","length(merged_df): 7805567\n","65 Champhai\n","length(merged_df): 8604961\n","66 Kolasib\n","length(merged_df): 9126897\n","67 Lawangtlai\n","length(merged_df): 9927838\n","68 Lunglei\n","length(merged_df): 11885061\n","69 Mamit\n","length(merged_df): 13041068\n","70 Saiha\n","length(merged_df): 13994266\n","71 Serchhip\n","length(merged_df): 14664286\n","72 Dimapur\n","length(merged_df): 14839149\n","73 Kiphire\n","length(merged_df): 15397224\n","74 Kohima\n","length(merged_df): 16046210\n","75 Longleng\n","length(merged_df): 16286450\n","76 Mokokchung\n","length(merged_df): 17059850\n","77 Mon\n","length(merged_df): 18007009\n","78 Peren\n","length(merged_df): 18737431\n","79 Phek\n","length(merged_df): 19621866\n","80 Tuensang\n","length(merged_df): 20699273\n","81 Wokha\n","length(merged_df): 21418859\n","82 Zunheboto\n","length(merged_df): 22249456\n","83 East Sikkim\n","84 North Sikkim\n","length(merged_df): 22255108\n","85 South Sikkim\n","length(merged_df): 22263543\n","86 West Sikkim\n","length(merged_df): 22433494\n","87 Dhalai\n","length(merged_df): 23093845\n","88 Gomati\n","length(merged_df): 23380456\n","89 Khowai\n","length(merged_df): 23629337\n","90 North Tripura\n","length(merged_df): 24031752\n","91 Sipahijala\n","length(merged_df): 24177507\n","92 South Tripura\n","length(merged_df): 24426953\n","93 Unokoti\n","length(merged_df): 24600290\n","94 West Tripura\n","length(merged_df): 24730615\n","95 Alipurduar\n","length(merged_df): 25183066\n","96 Darjiling\n","length(merged_df): 26125332\n","97 Jalpaiguri\n","length(merged_df): 26444567\n","98 Koch Bihar\n","length(merged_df): 26737087\n","Saving merged_corrections_1.csv\n"]},{"output_type":"stream","name":"stderr","text":["WARNING:google_auth_httplib2:httplib2 transport does not support per-request timeout. Set the timeout when constructing the httplib2.Http instance.\n","WARNING:google_auth_httplib2:httplib2 transport does not support per-request timeout. Set the timeout when constructing the httplib2.Http instance.\n"]},{"output_type":"stream","name":"stdout","text":["Upload complete.\n","Ingestion task started: 5a592b45-4bd1-4fef-9018-cd77a083c6de\n","2023\n","length(dist_list): 99\n","0 Anjaw\n","length(merged_df): 1848049\n","1 Changlang\n","length(merged_df): 4620455\n","2 Dibang Valley\n","length(merged_df): 7067896\n","3 East Kameng\n","length(merged_df): 9238528\n","4 East Siang\n","length(merged_df): 10273774\n","5 Kurung Kumey\n","length(merged_df): 12139221\n","6 Lohit\n","length(merged_df): 13411891\n","7 Longding\n","length(merged_df): 13957060\n","8 Lower Dibang Valley\n","length(merged_df): 15800235\n","9 Lower Subansiri\n","length(merged_df): 17537049\n","10 Namsai\n","length(merged_df): 17592906\n","11 Papum Pare\n","length(merged_df): 19454295\n","12 Tawang\n","length(merged_df): 20259605\n","13 Tirap\n","length(merged_df): 20849587\n","14 Upper Siang\n","length(merged_df): 25013318\n","15 Upper Subansiri\n","length(merged_df): 26200628\n","16 West Kameng\n","length(merged_df): 29142728\n","17 West Siang\n","length(merged_df): 31692865\n","18 Baksa\n","length(merged_df): 31844938\n","19 Barpeta\n","length(merged_df): 31948301\n","20 Bongaigaon\n","length(merged_df): 32020012\n","21 Cachar\n","length(merged_df): 33015996\n","22 Chirang\n","length(merged_df): 33110098\n","23 Darrang\n","length(merged_df): 33184191\n","24 Dhemaji\n","length(merged_df): 33459882\n","25 Dhubri\n","length(merged_df): 33584733\n","26 Dibrugarh\n","length(merged_df): 33854284\n","27 Dima Hasao\n","length(merged_df): 35107231\n","28 Goalpara\n","length(merged_df): 35181002\n","29 Golaghat\n","length(merged_df): 35345503\n","30 Hailakandi\n","length(merged_df): 35574708\n","31 Jorhat\n","length(merged_df): 35772826\n","32 Kamrup Metropolitan\n","length(merged_df): 35930324\n","33 Kamrup\n","length(merged_df): 36346509\n","34 Karbi Anglong\n","length(merged_df): 38769946\n","35 Karimganj\n","length(merged_df): 39093925\n","36 Kokrajhar\n","length(merged_df): 39620058\n","37 Lakhimpur\n","length(merged_df): 39918127\n","38 Morigaon\n","length(merged_df): 40003240\n","39 Nagaon\n","length(merged_df): 40244487\n","40 Nalbari\n","length(merged_df): 40300500\n","41 Sivasagar\n","length(merged_df): 40499034\n","42 Sonitpur\n","length(merged_df): 40866091\n","43 Tinsukia\n","length(merged_df): 41475492\n","44 Udalguri\n","length(merged_df): 41567105\n","45 Bishnupur\n","length(merged_df): 41604433\n","46 Chandel\n","length(merged_df): 42744913\n","47 Churachandpur\n","length(merged_df): 43872322\n","48 Imphal East\n","length(merged_df): 43964012\n","49 Imphal West\n","length(merged_df): 44013228\n","50 Senapati\n","length(merged_df): 45464578\n","51 Tamenglong\n","length(merged_df): 47644037\n","52 Thoubal\n","length(merged_df): 47707540\n","53 Ukhrul\n","length(merged_df): 50022798\n","Saving merged_corrections_0.csv\n","Upload complete.\n","Ingestion task started: bf24e7ae-59e0-47e6-9e17-ceda97654ae2\n","54 East Garo Hills\n","length(merged_df): 357834\n","55 East Khasi Hills\n","length(merged_df): 1398969\n","56 Jaintia Hills\n","length(merged_df): 2608139\n","57 North Garo Hills\n","length(merged_df): 2680692\n","58 Ri Bhoi\n","length(merged_df): 3610989\n","59 South Garo Hills\n","length(merged_df): 4039361\n","60 South West Garo Hills\n","length(merged_df): 4071718\n","61 South West Khasi Hills\n","length(merged_df): 4490566\n","62 West Garo Hills\n","length(merged_df): 4804823\n","63 West Khasi Hills\n","length(merged_df): 6123187\n","64 Aizawl\n","length(merged_df): 7805567\n","65 Champhai\n","length(merged_df): 8604961\n","66 Kolasib\n","length(merged_df): 9126897\n","67 Lawangtlai\n","length(merged_df): 9927838\n","68 Lunglei\n","length(merged_df): 11885061\n","69 Mamit\n","length(merged_df): 13041068\n","70 Saiha\n","length(merged_df): 13994266\n","71 Serchhip\n","length(merged_df): 14664286\n","72 Dimapur\n","length(merged_df): 14839149\n","73 Kiphire\n","length(merged_df): 15397224\n","74 Kohima\n","length(merged_df): 16046210\n","75 Longleng\n","length(merged_df): 16286450\n","76 Mokokchung\n","length(merged_df): 17059850\n","77 Mon\n","length(merged_df): 18007009\n","78 Peren\n","length(merged_df): 18737431\n","79 Phek\n","length(merged_df): 19621866\n","80 Tuensang\n","length(merged_df): 20699273\n","81 Wokha\n","length(merged_df): 21418859\n","82 Zunheboto\n","length(merged_df): 22249456\n","83 East Sikkim\n","84 North Sikkim\n","length(merged_df): 22255108\n","85 South Sikkim\n","length(merged_df): 22263543\n","86 West Sikkim\n","length(merged_df): 22433494\n","87 Dhalai\n","length(merged_df): 23093845\n","88 Gomati\n","length(merged_df): 23380456\n","89 Khowai\n","length(merged_df): 23629337\n","90 North Tripura\n","length(merged_df): 24031752\n","91 Sipahijala\n","length(merged_df): 24177507\n","92 South Tripura\n","length(merged_df): 24426953\n","93 Unokoti\n","length(merged_df): 24600290\n","94 West Tripura\n","length(merged_df): 24730615\n","95 Alipurduar\n","length(merged_df): 25183066\n","96 Darjiling\n","length(merged_df): 26125332\n","97 Jalpaiguri\n","length(merged_df): 26444567\n","98 Koch Bihar\n","length(merged_df): 26737087\n","Saving merged_corrections_1.csv\n","Upload complete.\n","Ingestion task started: 7643f5cd-7037-483d-97cd-4712f77c7924\n"]}],"source":["# CH\n","\n","# Function to convert string representation of list to an actual list\n","def convert_to_list(string):\n"," return ast.literal_eval(string)\n","\n","for agroclimatic_zone in acz_list:\n"," print(agroclimatic_zone)\n"," for year in years:\n"," print(year)\n"," df = pd.read_csv('drive/MyDrive/TreeHealth/district_to_agroclimaticZone_mapping.csv')\n"," df['IntersectingZones'] = df['IntersectingZones'].apply(convert_to_list)\n"," district_mapping_df = df[df['AgroclimaticZone'] == agroclimatic_zone][['District', 'IntersectingZones']]\n"," dist_list = []\n"," for ind in district_mapping_df.index:\n"," district = district_mapping_df.loc[ind, 'District']\n"," zones = district_mapping_df['IntersectingZones'][ind]\n"," dist_list.append(district)\n","\n"," print(f'length(dist_list): {len(dist_list)}')\n","\n"," correction_num = 0\n"," j = 0\n"," merged_df = pd.DataFrame()\n","\n","\n"," for district in dist_list:\n"," print(j, district)\n"," j += 1\n","\n"," try:\n"," df = pd.read_csv(f'drive/MyDrive/TreeHealth/{agroclimatic_zone}/{district}/{year}/result_chm_corrections.csv')\n"," except:\n"," continue\n","\n"," merged_df = pd.concat([merged_df, df], ignore_index=True)\n"," print(f'length(merged_df): {len(merged_df)}')\n"," drive_path = f'drive/MyDrive/TreeHealth/{agroclimatic_zone}/corrections_chm_{year}_{correction_num}.csv'\n","\n"," if len(merged_df) > 50000000: # Adjust based on need, if script crashes because of RAM exhaustion, default 70000000\n"," cols = merged_df.columns\n"," for i in range(1, len(cols)):\n"," merged_df[cols[i]] = merged_df[cols[i]].astype('Int64')\n"," print(f'Saving merged_corrections_{correction_num}.csv')\n"," merged_df.to_csv(drive_path, index=False)\n","\n"," file_name = f\"corrections_ch_{year}_result_{correction_num}_{agroclimaticZone_acronym_dict[agroclimatic_zone]}\"\n"," upload_file_to_gcs(drive_path, file_name)\n"," export_to_gee(file_name)\n","\n"," del(merged_df)\n"," correction_num += 1\n"," merged_df = pd.DataFrame()\n","\n"," if len(merged_df) > 0:\n"," cols = merged_df.columns\n"," for i in range(1, len(cols)):\n"," merged_df[cols[i]] = merged_df[cols[i]].astype('Int64')\n"," print(f'Saving merged_corrections_{correction_num}.csv')\n"," merged_df.to_csv(drive_path, index=False)\n","\n"," file_name = f\"corrections_ch_{year}_result_{correction_num}_{agroclimaticZone_acronym_dict[agroclimatic_zone]}\"\n"," upload_file_to_gcs(drive_path, file_name)\n"," export_to_gee(file_name)\n","\n"," del(merged_df)"]}],"metadata":{"colab":{"collapsed_sections":["Ah69USIF_hSV"],"provenance":[{"file_id":"1m0Q6Ic94NeVThpU_ZU0OydAeqlp6zWoe","timestamp":1759050819148}]},"kernelspec":{"display_name":"Python 3","name":"python3"},"language_info":{"name":"python"}},"nbformat":4,"nbformat_minor":0} \ No newline at end of file diff --git a/utilities/scripts/tree_health/gee_scripts/ccd_change.js b/utilities/scripts/tree_health/gee_scripts/ccd_change.js new file mode 100644 index 00000000..33e60087 --- /dev/null +++ b/utilities/scripts/tree_health/gee_scripts/ccd_change.js @@ -0,0 +1,76 @@ +// This script is for doing a modal analysis while analysing forest cover change over the years between any 2 given years. +// It also gives the change quantification + +// Set year_1 and year_2. These are the 2 years between which comparison has to be made +// Note that year_2 must be > year_1 and both must be of Integer type +var year_1 = 2017; +var year_2 = 2023; + +var india_boundary = ee.FeatureCollection("projects/ext-datasets/assets/datasets/ACZs"); + +var agroclimaticZoneAcronymDict = { + 'Eastern Plateau & Hills Region': 'EPAHR', + 'Southern Plateau and Hills Region': 'SPAHR', + 'East Coast Plains & Hills Region': 'ECPHR', + 'Western Plateau and Hills Region': 'WPAHR', + 'Central Plateau & Hills Region': 'CPAHR', + 'Lower Gangetic Plain Region': 'LGPR', + 'Middle Gangetic Plain Region': 'MGPR', + 'Upper Gangetic Plain Region': 'UGPR', + 'Trans Gangetic Plain Region': 'TGPR', + 'Eastern Himalayan Region': 'EHR', + 'Western Himalayan Region': 'WHR' +}; + +// var acz = 'Eastern Plateau & Hills Region'; +// var acz = 'Lower Gangetic Plain Region'; +// var acz = 'Western Himalayan Region'; +// var acz = 'Eastern Himalayan Region'; +// var acz = 'Upper Gangetic Plain Region'; +var acz = 'Middle Gangetic Plain Region'; +// var acz = 'Trans Gangetic Plain Region'; +// var acz = 'Central Plateau & Hills Region'; +// var acz = 'Western Plateau and Hills Region'; +// var acz = 'Southern Plateau and Hills Region'; +// var acz = 'East Coast Plains & Hills Region'; + +// Set the AOI +var aoi = india_boundary.filter(ee.Filter.eq('regionname', acz)).geometry(); + +var project_path = 'projects/corestack-trees/assets/tree_characteristics'; + +var initial_image = ee.Image(project_path+'/modal_ccd_' + year_1 + '/' + agroclimaticZoneAcronymDict[acz]); +var final_image = ee.Image(project_path+'/modal_ccd_' + year_2 + '/' + agroclimaticZoneAcronymDict[acz]); + +var change = initial_image.addBands(final_image); +change = change.unmask(-9999); + +print(change); + +// 3 denotes missing data +change = change.expression( + "((b('cc')==0) and (b('cc_1')==0)) ? (0)"+ + ":((b('cc')==1) and (b('cc_1')==1)) ? (0)"+ + ":((b('cc')==0) and (b('cc_1')==1)) ? (1)"+ + ":((b('cc')==1) and (b('cc_1')==0)) ? (-1)"+ + ":((b('cc')==-9999) and (b('cc_1')!=-9999)) ? (2)"+ + ":((b('cc')!=-9999) and (b('cc_1')==-9999)) ? (-2)"+ + ":((b('cc')==2) or (b('cc_1')==2)) ? (3)"+ + ":-9999" + ) + .clip(aoi); +change = change.updateMask(change.neq(-9999)); +// var palette = ['red', 'orange', 'white', 'lightgreen', 'darkgreen', 'black']; +var palette = ['FF0000', 'FFA500', 'FFFFFF', '8AFF8A', '007500', '000000']; +Map.addLayer(change, {min: -2, max: 3, palette: palette}, 'change layer'); + +// Export CCD change image +Export.image.toAsset({ + image: change, + description: 'ccd_change_' + agroclimaticZoneAcronymDict[acz], + assetId: project_path+'/ccd_change_' + year_1 + '_' + year_2 + '/' + agroclimaticZoneAcronymDict[acz], + region: aoi, + scale: 25, + crs: 'EPSG:4326', + maxPixels: 10000000000 + }); \ No newline at end of file diff --git a/utilities/scripts/tree_health/gee_scripts/ch_change.js b/utilities/scripts/tree_health/gee_scripts/ch_change.js new file mode 100644 index 00000000..ec746fd3 --- /dev/null +++ b/utilities/scripts/tree_health/gee_scripts/ch_change.js @@ -0,0 +1,74 @@ +// This script is for doing a modal analysis while analysing forest cover change over the years between any 2 given years. +// It also gives the change quantification + +// Set year_1 and year_2. These are the 2 years between which comparison has to be made +// Note that year_2 must be > year_1 and both must be of Integer type +var year_1 = 2017; +var year_2 = 2023; + +var india_boundary = ee.FeatureCollection("projects/ext-datasets/assets/datasets/ACZs"); + +var agroclimaticZoneAcronymDict = { + 'Eastern Plateau & Hills Region': 'EPAHR', + 'Southern Plateau and Hills Region': 'SPAHR', + 'East Coast Plains & Hills Region': 'ECPHR', + 'Western Plateau and Hills Region': 'WPAHR', + 'Central Plateau & Hills Region': 'CPAHR', + 'Lower Gangetic Plain Region': 'LGPR', + 'Middle Gangetic Plain Region': 'MGPR', + 'Upper Gangetic Plain Region': 'UGPR', + 'Trans Gangetic Plain Region': 'TGPR', + 'Eastern Himalayan Region': 'EHR', + 'Western Himalayan Region': 'WHR' +}; + +// var acz = 'Eastern Plateau & Hills Region'; +// var acz = 'Lower Gangetic Plain Region'; +// var acz = 'Western Himalayan Region'; +// var acz = 'Eastern Himalayan Region'; +// var acz = 'Upper Gangetic Plain Region'; +var acz = 'Middle Gangetic Plain Region'; +// var acz = 'Trans Gangetic Plain Region'; +// var acz = 'Central Plateau & Hills Region'; +// var acz = 'Western Plateau and Hills Region'; +// var acz = 'Southern Plateau and Hills Region'; +// var acz = 'East Coast Plains & Hills Region'; + +// Set the AOI +var aoi = india_boundary.filter(ee.Filter.eq('regionname', acz)).geometry(); + +var project_path = 'projects/corestack1-dev-alpha/assets/tree_characteristics'; + +var initial_image = ee.Image(project_path+'/modal_ch_' + year_1 + '/' + agroclimaticZoneAcronymDict[acz]); +var final_image = ee.Image(project_path+'/modal_ch_' + year_2 + '/' + agroclimaticZoneAcronymDict[acz]); + +var change = initial_image.addBands(final_image); +change = change.unmask(-9999); +// 3 denotes missing data +change = change.expression( + "((b('ch_class') == -9999) and (b('ch_class_1') != -9999)) ? (2)" + + ": ((b('ch_class') != -9999) and (b('ch_class_1') == -9999)) ? (-2)"+ + ": ((b('ch_class') == 8) or (b('ch_class_1') == 8)) ? (3)" + + ": ((b('ch_class') == b('ch_class_1')) and (b('ch_class') != -9999)) ? (0)" + + ": ((b('ch_class') < b('ch_class_1')) and (b('ch_class') != -9999) and ((b('ch_class') <= 1) or (b('ch_class') >= 4))) ? (1)" + + ": ((b('ch_class') > b('ch_class_1')) and (b('ch_class_1') != -9999)) ? (-1)" + + ": ((b('ch_class') == 2) and ((b('ch_class_1') == 3) or (b('ch_class_1') == 6) or (b('ch_class_1') == 7))) ? (1)" + + ": ((b('ch_class') == 3) and ((b('ch_class_1') == 6) or (b('ch_class_1') == 7))) ? (1)" + + ": (((b('ch_class') == 2) or (b('ch_class') == 3)) and ((b('ch_class_1') == 4) or (b('ch_class_1') == 5))) ? (-1)"+ + ":-9999" +).clip(aoi); +change = change.updateMask(change.neq(-9999)); +// var palette = ['red', 'orange', 'white', 'lightgreen', 'green', 'black']; +var palette = ['FF0000', 'FFA500', 'FFFFFF', '8AFF8A', '007500', '000000']; +Map.addLayer(change, {min: -2, max: 3, palette: palette}, 'change layer ch'); + +// Export CH change image +Export.image.toAsset({ + image: change, + description: 'ch_change_' + agroclimaticZoneAcronymDict[acz], + assetId: project_path+'/ch_change_' + year_1 + '_' + year_2 + '/' + agroclimaticZoneAcronymDict[acz], + region: aoi, + scale: 25, + crs: 'EPSG:4326', + maxPixels: 10000000000 + }); diff --git a/utilities/scripts/tree_health/gee_scripts/fc_to_image.js b/utilities/scripts/tree_health/gee_scripts/fc_to_image.js new file mode 100644 index 00000000..629ce8ec --- /dev/null +++ b/utilities/scripts/tree_health/gee_scripts/fc_to_image.js @@ -0,0 +1,157 @@ +// Using Compiled Result from upload asset script +// Both CCD and CH images can be generated using this script. Comment out CCD if you +// are generating CH image and vice versa + +// TODO: Set total_files according to the number of result files for a particular ACZ +// in a particular year +var total_files = 5; + +// TODO: Mention the year for which you want to run this script for. +var year = '2018'; + +// TODO: Uncomment the acz for which you want to run this script for. +// var acz = 'Eastern Plateau & Hills Region'; +// var acz = 'Middle Gangetic Plain Region'; +// var acz = 'Lower Gangetic Plain Region'; +// var acz = 'Western Himalayan Region'; +var acz = 'Eastern Himalayan Region'; +// var acz = 'Upper Gangetic Plain Region'; +// var acz = 'Trans Gangetic Plain Region'; +// var acz = 'Central Plateau & Hills Region'; +// var acz = 'Western Plateau and Hills Region'; +// var acz = 'Southern Plateau and Hills Region'; +// var acz = 'East Coast Plains & Hills Region'; + +var res_list = []; +for (var i=0; i modify these + region: aoi, + scale: 25, + crs: 'EPSG:4326', + maxPixels: 10000000000 + }); +} + + + +// ch + +var dist_list_dict = {'EPAHR': ['Baloda Bazar', 'Balod', 'BalrampurC', 'Bastar', 'Bemetara', 'BijapurC', 'BilaspurC', 'Dantewada', 'Dhamtari', 'Durg', 'Gariaband', 'Janjgir-Champa', 'Jashpur', 'Kabeerdham', 'Kondagaon', 'Korba', 'Koriya', 'Mahasamund', 'Mungeli', 'Narayanpur', 'Raigarh', 'Raipur', 'Rajnandgaon', 'Sukma', 'Surajpur', 'Surguja', 'Uttar Bastar Kanker', 'Bokaro', 'Chatra', 'Deoghar', 'Dhanbad', 'Dumka', 'Garhwa', 'Giridih', 'Gumla', 'Hazaribagh', 'Jamtara', 'Khunti', 'Kodarma', 'Latehar', 'Lohardaga', 'Pakur', 'Palamu', 'Pashchimi Singhbhum', 'Purbi Singhbhum', 'Ramgarh', 'Ranchi', 'Saraikela-kharsawan', 'Simdega', 'Anuppur', 'Balaghat', 'Shahdol', 'Sidhi', 'Singrauli', 'Umaria', 'Bhandara', 'Chandrapur', 'Garhchiroli', 'Gondiya', 'Anugul', 'Balangir', 'Bargarh', 'Bauda', 'Debagarh', 'Dhenkanal', 'Jharsuguda', 'Kalahandi', 'Kandhamal', 'Kendujhar', 'Koraput', 'Malkangiri', 'Mayurbhanj', 'Nabarangapur', 'Nuapada', 'Rayagada', 'Sambalpur', 'Subarnapur', 'Sundargarh', 'Puruliya'], + 'CPAHR': ['Ashoknagar', 'Betul', 'Bhind', 'Bhopal', 'Chhatarpur', 'Chhindwara', 'Damoh', 'Datia', 'Dindori', 'Guna', 'Gwalior', 'Harda', 'Hoshangabad', 'Jabalpur', 'Katni', 'Mandla', 'Morena', 'Narsimhapur', 'Panna', 'Raisen', 'Rajgarh', 'Rewa', 'Sagar', 'Satna', 'Sehore', 'Seoni', 'Sheopur', 'Shivpuri', 'Tikamgarh', 'Vidisha', 'Ajmer', 'Alwar', 'Banswara', 'Baran', 'Bharatpur', 'Bhilwara', 'Bundi', 'Chittaurgarh', 'Dausa', 'Dhaulpur', 'Dungarpur', 'Jaipur', 'Karauli', 'Kota', 'Pali', 'Pratapgarhraj', 'Rajsamand', 'Sawai Madhopur', 'Sirohi', 'Tonk', 'Udaipur', 'Banda', 'Chitrakoot', 'Hamirpurup', 'Jalaun', 'Jhansi', 'Lalitpur', 'Mahoba'], + 'SPAHR': ['Anantapur', 'Chittoor', 'Kurnool', 'Y.S.R.', 'Bagalkot', 'Bangalore Rural', 'Bangalore', 'Belgaum', 'Bellary', 'Bidar', 'Bijapur', 'Chamrajnagar', 'Chikballapura', 'Chitradurga', 'Davanagere', 'Dharwad', 'Gadag', 'Gulbarga', 'Hassan', 'Haveri', 'Kolar', 'Koppal', 'Mandya', 'Mysore', 'Raichur', 'Ramanagara', 'Tumkur', 'Yadgir', 'Ariyalur', 'Coimbatore', 'Dharmapuri', 'Dindigul', 'Erode', 'Karur', 'Krishnagiri', 'Madurai', 'Namakkal', 'Perambalur', 'Salem', 'The Nilgiris', 'Theni', 'Tiruchirappalli', 'Tiruppur', 'Adilabad', 'Hyderabad', 'Karimnagar', 'Khammam', 'Mahbubnagar', 'Medak', 'Nalgonda', 'Nizamabad', 'Ranga Reddy', 'Warangal'], + 'WPAHR': ['Agar Malwa', 'Alirajpur', 'Barwani', 'Burhanpur', 'Dewas', 'Dhar', 'East Nimar', 'Indore', 'Jhabua', 'Mandsaur', 'Neemuch', 'Ratlam', 'Shajapur', 'Ujjain', 'West Nimar', 'Ahmadnagar', 'Akola', 'Amravati', 'Aurangabad', 'Bid', 'Buldana', 'Dhule', 'Hingoli', 'Jalgaon', 'Jalna', 'Kolhapur', 'Latur', 'Nagpur', 'Nanded', 'Nandurbar', 'Nashik', 'Osmanabad', 'Parbhani', 'Pune', 'Sangli', 'Satara', 'Solapur', 'Wardha', 'Washim', 'Yavatmal', 'Jhalawar'], + 'ECPHR': ['East Godavari', 'Guntur', 'Krishna', 'Nellore', 'Prakasam', 'Srikakulam', 'Visakhapatnam', 'Vizianagaram', 'West Godavari', 'Baleshwar', 'Bhadrak', 'Cuttack', 'Gajapati', 'Ganjam', 'Jagatsinghapur', 'Jajapur', 'Kendrapara', 'Khordha', 'Nayagarh', 'Puri', 'Karaikal', 'Puducherry', 'Yanam', 'Chennai', 'Cuddalore', 'Kancheepuram', 'Nagappattinam', 'Pudukkottai', 'Ramanathapuram', 'Sivaganga', 'Thanjavur', 'Thiruvallur', 'Thiruvarur', 'Thoothukkudi', 'Tirunelveli', 'Tiruvannamalai', 'Vellore', 'Viluppuram', 'Virudunagar'], + 'UGPR': ['Agra', 'Aligarh', 'Allahabad', 'Amethi', 'Amroha', 'Auraiya', 'Baghpat', 'Barabanki', 'Bareilly', 'Bijnor', 'Budaun', 'Bulandshahr', 'Etah', 'Etawah', 'Farrukhabad', 'Fatehpur', 'Firozabad', 'Gautam Buddha Nagar', 'Ghaziabad', 'Hapur', 'Hardoi', 'Hathras', 'Kannauj', 'Kanpur Dehat', 'Kanpur Nagar', 'Kasganj', 'Kaushambi', 'Lakhimpur Kheri', 'Lucknow', 'Mainpuri', 'Mathura', 'Meerut', 'Moradabad', 'Muzaffarnagar', 'Pilibhit', 'Pratapgarhup', 'Rae Bareli', 'Rampur', 'Saharanpur', 'Sambhal', 'Shahjahanpur', 'Shamli', 'Sitapur', 'Sultanpur', 'Unnao', 'Hardwar', 'Udham Singh Nagar'], + 'LGPR': ['Bankura', 'Barddhaman', 'Birbhum', 'Dakshin Dinajpur', 'Haora', 'Hugli', 'Kolkata', 'Maldah', 'Murshidabad', 'Nadia', 'North 24 Parganas', 'Pashchim Medinipur', 'Purba Medinipur', 'South 24 Parganas', 'Uttar Dinajpur'], + 'MGPR': ['Araria', 'Arwal', 'AurangabadB', 'Banka', 'Begusarai', 'Bhagalpur', 'Bhojpur', 'Buxar', 'Darbhanga', 'Gaya', 'Gopalganj', 'Jamui', 'Jehanabad', 'Kaimur', 'Katihar', 'Khagaria', 'Kishanganj', 'Lakhisarai', 'Madhepura', 'Madhubani', 'Munger', 'Muzaffarpur', 'Nalanda', 'Nawada', 'Pashchim Champaran', 'Patna', 'Purba Champaran', 'Purnia', 'Rohtas', 'Saharsa', 'Samastipur', 'Saran', 'Sheikhpura', 'Sheohar', 'Sitamarhi', 'Siwan', 'Supaul', 'Vaishali', 'Godda', 'Sahibganj', 'Ambedkar Nagar', 'Azamgarh', 'Bahraich', 'Ballia', 'Balrampur', 'Basti', 'Chandauli', 'Deoria', 'Faizabad', 'Ghazipur', 'Gonda', 'Gorakhpur', 'Jaunpur', 'Kushinagar', 'Maharajganj', 'Mau', 'Mirzapur', 'Sant Kabir Nagar', 'Sant Ravi Das Nagar', 'Shravasti', 'Siddharth Nagar', 'Sonbhadra', 'Varanasi'], + 'TGPR': ['Chandigarh', 'Ambala', 'Bhiwani', 'Faridabad', 'Fatehabad', 'Gurgaon', 'Hisar', 'Jhajjar', 'Jind', 'Kaithal', 'Karnal', 'Kurukshetra', 'Mahendragarh', 'Mewat', 'Palwal', 'Panchkula', 'Panipat', 'Rewari', 'Rohtak', 'Sirsa', 'Sonipat', 'Yamunanagar', 'Delhi', 'Amritsar', 'Barnala', 'Bathinda', 'Faridkot', 'Fatehgarh Sahib', 'Fazilka', 'Firozpur', 'Gurdaspur', 'Hoshiarpur', 'Jalandhar', 'Kapurthala', 'Ludhiana', 'Mansa', 'Moga', 'Muktsar', 'Patiala', 'Sahibzada Ajit Singh Nagar', 'Sangrur', 'Shahid Bhagat Singh Nagar', 'Tarn Taran', 'Ganganagar', 'Hanumangarh'], + 'WHR': ['Bilaspur', 'Chamba', 'Hamirpur', 'Kangra', 'Kinnaur', 'Kullu', 'Lahul & Spiti', 'Mandi', 'Shimla', 'Sirmaur', 'Solan', 'Una', 'Anantnag', 'Badgam', 'Bandipore', 'Baramulla', 'Doda', 'Ganderbal', 'Jammu', 'Kargil', 'Kathua', 'Kishtwar', 'Kulgam', 'Kupwara', 'Leh (Ladakh)', 'Poonch', 'Pulwama', 'Rajouri', 'Ramban', 'Reasi', 'Samba', 'Shupiyan', 'Srinagar', 'Udhampur', 'Pathankot', 'Rupnagar', 'Almora', 'Bageshwar', 'Chamoli', 'Champawat', 'Dehradun', 'Garhwal', 'Nainital', 'Pithoragarh', 'Rudraprayag', 'Tehri Garhwal', 'Uttarkashi'], + 'EHR': ['Anjaw', 'Changlang', 'Dibang Valley', 'East Kameng', 'East Siang', 'Kurung Kumey', 'Lohit', 'Longding', 'Lower Dibang Valley', 'Lower Subansiri', 'Namsai', 'Papum Pare', 'Tawang', 'Tirap', 'Upper Siang', 'Upper Subansiri', 'West Kameng', 'West Siang', 'Baksa', 'Barpeta', 'Bongaigaon', 'Cachar', 'Chirang', 'Darrang', 'Dhemaji', 'Dhubri', 'Dibrugarh', 'Dima Hasao', 'Goalpara', 'Golaghat', 'Hailakandi', 'Jorhat', 'Kamrup Metropolitan', 'Kamrup', 'Karbi Anglong', 'Karimganj', 'Kokrajhar', 'Lakhimpur', 'Morigaon', 'Nagaon', 'Nalbari', 'Sivasagar', 'Sonitpur', 'Tinsukia', 'Udalguri', 'Bishnupur', 'Chandel', 'Churachandpur', 'Imphal East', 'Imphal West', 'Senapati', 'Tamenglong', 'Thoubal', 'Ukhrul', 'East Garo Hills', 'East Khasi Hills', 'Jaintia Hills', 'North Garo Hills', 'Ri Bhoi', 'South Garo Hills', 'South West Garo Hills', 'South West Khasi Hills', 'West Garo Hills', 'West Khasi Hills', 'Aizawl', 'Champhai', 'Kolasib', 'Lawangtlai', 'Lunglei', 'Mamit', 'Saiha', 'Serchhip', 'Dimapur', 'Kiphire', 'Kohima', 'Longleng', 'Mokokchung', 'Mon', 'Peren', 'Phek', 'Tuensang', 'Wokha', 'Zunheboto', 'East Sikkim', 'North Sikkim', 'South Sikkim', 'West Sikkim', 'Dhalai', 'Gomati', 'Khowai', 'North Tripura', 'Sipahijala', 'South Tripura', 'Unokoti', 'West Tripura', 'Alipurduar', 'Darjiling', 'Jalpaiguri', 'Koch Bihar'] +}; + +acz = acronym; + +var dist_list = dist_list_dict[acz]; +var india_district = ee.FeatureCollection('projects/ee-indiasat/assets/india_district_boundaries'); +var aoi = india_district.filter(ee.Filter.eq('Name', dist_list[0])).geometry(); + +for (var i=1; i year_1 and both must be of Integer type +var year_1 = 2017; +var year_2 = 2018; + +// var india_district_boundary = ee.FeatureCollection("projects/ee-indiasat/assets/india_district_boundaries"); +// var aoi = india_district_boundary.filter(ee.Filter.eq('Name', "Baloda Bazar")).geometry(); + +var india_boundary = ee.FeatureCollection("projects/ext-datasets/assets/datasets/ACZs"); + +var agroclimaticZoneAcronymDict = { + 'Eastern Plateau & Hills Region': 'EPAHR', + 'Southern Plateau and Hills Region': 'SPAHR', + 'East Coast Plains & Hills Region': 'ECPHR', + 'Western Plateau and Hills Region': 'WPAHR', + 'Central Plateau & Hills Region': 'CPAHR', + 'Lower Gangetic Plain Region': 'LGPR', + 'Middle Gangetic Plain Region': 'MGPR', + 'Upper Gangetic Plain Region': 'UGPR', + 'Trans Gangetic Plain Region': 'TGPR', + 'Eastern Himalayan Region': 'EHR', + 'Western Himalayan Region': 'WHR' +}; + +// var acz = 'Eastern Plateau & Hills Region'; +// var acz = 'Lower Gangetic Plain Region'; +// var acz = 'Western Himalayan Region'; +// var acz = 'Eastern Himalayan Region'; +// var acz = 'Upper Gangetic Plain Region'; +// var acz = 'Middle Gangetic Plain Region'; +// var acz = 'Trans Gangetic Plain Region'; +// var acz = 'Central Plateau & Hills Region'; +// var acz = 'Western Plateau and Hills Region'; +// var acz = 'Southern Plateau and Hills Region'; +var acz = 'East Coast Plains & Hills Region'; + +// Set the AOI +var aoi = india_boundary.filter(ee.Filter.eq('regionname', acz)).geometry(); + +// Set the change list according to the following: +// -2 -> Deforestation +// -1 -> Degradation +// 0 -> No Change +// 1 -> Improvement +// 2 -> Afforestation +// 3 -> Missing Data +var change_list = [-1, 1]; + +Map.addLayer(aoi, {'color': 'black'}, 'AOI'); +Map.centerObject(aoi, 7); + +var year_in_0 = String(year_1-1); +var year_in_1 = String(year_1); +var year_in_2 = String(year_1+1); + +var year_fi_0 = String(year_2-1); +var year_fi_1 = String(year_2); +var year_fi_2 = String(year_2+1); + +var project_path = 'projects/corestack-trees/assets/tree_characteristics'; + +var ccd_in_0 = ee.ImageCollection(project_path+'/ccd_' + year_in_0) + .filterBounds(aoi) + .map(function(img) { + return img.select('cc').toInt32(); + }); + + +ccd_in_0 = ee.Algorithms.If( + ccd_in_0.size().eq(0), + // Dummy masked image + ee.Image.constant(-9999) + .rename('cc') + .reproject('EPSG:4326', null, 25) + .clip(aoi) + .updateMask(ee.Image(1)), + // Real mean image + ccd_in_0.mean().clip(aoi).select('cc') +); + +ccd_in_0 = ee.Image(ccd_in_0); + + +// var tree_cover_in_0 = ee.ImageCollection('projects/ee-mtpictd/assets/harsh/tree_cover_' + year_in_0) +// .filterBounds(aoi) +// .mode() +// .clip(aoi); +var tree_cover_in_0 = tree_cover.get_tree_cover(aoi, year_in_0); +tree_cover_in_0 = tree_cover_in_0.rename(['label_' + year_in_0.slice(-2)]); + + +var ccd_in_1 = ee.ImageCollection(project_path+'/ccd_' + year_in_1) + .filterBounds(aoi) + .map(function(img) { + return img.select('cc').toInt32(); + }); + +ccd_in_1 = ee.Algorithms.If( + ccd_in_1.size().eq(0), + // Dummy masked image + ee.Image.constant(-9999) + .rename('cc') + .reproject('EPSG:4326', null, 25) + .clip(aoi) + .updateMask(ee.Image(1)), + // Real mean image + ccd_in_1.mean().clip(aoi).select('cc') +); +ccd_in_1 = ee.Image(ccd_in_1); + +// var tree_cover_in_1 = ee.ImageCollection('projects/ee-mtpictd/assets/harsh/tree_cover_' + year_in_1) +// .filterBounds(aoi) +// .mode() +// .clip(aoi); +var tree_cover_in_1 = tree_cover.get_tree_cover(aoi, year_in_1); +tree_cover_in_1 = tree_cover_in_1.rename(['label_' + year_in_1.slice(-2)]); + +var ccd_in_2 = ee.ImageCollection(project_path+'/ccd_' + year_in_2) + .filterBounds(aoi) + .map(function(img) { + return img.select('cc').toInt32(); + }); + +ccd_in_2 = ee.Algorithms.If( + ccd_in_2.size().eq(0), + // Dummy masked image + ee.Image.constant(-9999) + .rename('cc') + .reproject('EPSG:4326', null, 25) + .clip(aoi) + .updateMask(ee.Image(1)), + // Real mean image + ccd_in_2.mean().clip(aoi).select('cc') +); +ccd_in_2 = ee.Image(ccd_in_2); + +// var tree_cover_in_2 = ee.ImageCollection('projects/ee-mtpictd/assets/harsh/tree_cover_' + year_in_2) +// .filterBounds(aoi) +// .mode() +// .clip(aoi); +var tree_cover_in_2 = tree_cover.get_tree_cover(aoi, year_in_2); +tree_cover_in_2 = tree_cover_in_2.rename(['label_' + year_in_2.slice(-2)]); + +var ccd_fi_0 = ee.ImageCollection(project_path+'/ccd_' + year_fi_0) + .filterBounds(aoi) + .map(function(img) { + return img.select('cc').toInt32(); + }); + +ccd_fi_0 = ee.Algorithms.If( + ccd_fi_0.size().eq(0), + // Dummy masked image + ee.Image.constant(-9999) + .rename('cc') + .reproject('EPSG:4326', null, 25) + .clip(aoi) + .updateMask(ee.Image(1)), + // Real mean image + ccd_fi_0.mean().clip(aoi).select('cc') +); +ccd_fi_0 = ee.Image(ccd_fi_0); + +// var tree_cover_fi_0 = ee.ImageCollection('projects/ee-mtpictd/assets/harsh/tree_cover_' + year_fi_0) +// .filterBounds(aoi) +// .mode() +// .clip(aoi); +var tree_cover_fi_0 = tree_cover.get_tree_cover(aoi, year_fi_0); +tree_cover_fi_0 = tree_cover_fi_0.rename(['label_' + year_fi_0.slice(-2)]); + +var ccd_fi_1 = ee.ImageCollection(project_path+'/ccd_' + year_fi_1) + .filterBounds(aoi) + .map(function(img) { + return img.select('cc').toInt32(); + }); + +ccd_fi_1 = ee.Algorithms.If( + ccd_fi_1.size().eq(0), + // Dummy masked image + ee.Image.constant(-9999) + .rename('cc') + .reproject('EPSG:4326', null, 25) + .clip(aoi) + .updateMask(ee.Image(1)), + // Real mean image + ccd_fi_1.mean().clip(aoi).select('cc') +); +ccd_fi_1 = ee.Image(ccd_fi_1); + +// var tree_cover_fi_1 = ee.ImageCollection('projects/ee-mtpictd/assets/harsh/tree_cover_' + year_fi_1) +// .filterBounds(aoi) +// .mode() +// .clip(aoi); +var tree_cover_fi_1 = tree_cover.get_tree_cover(aoi, year_fi_1); +tree_cover_fi_1 = tree_cover_fi_1.rename(['label_' + year_fi_1.slice(-2)]); + +var ccd_fi_2 = ee.ImageCollection(project_path+'/ccd_' + year_fi_2) + .filterBounds(aoi) + .map(function(img) { + return img.select('cc').toInt32(); + }); + +ccd_fi_2 = ee.Algorithms.If( + ccd_fi_2.size().eq(0), + // Dummy masked image + ee.Image.constant(-9999) + .rename('cc') + .reproject('EPSG:4326', null, 25) + .clip(aoi) + .updateMask(ee.Image(1)), + // Real mean image + ccd_fi_2.mean().clip(aoi).select('cc') +); +ccd_fi_2 = ee.Image(ccd_fi_2); + +// var tree_cover_fi_2 = ee.ImageCollection('projects/ee-mtpictd/assets/harsh/tree_cover_' + year_fi_2) +// .filterBounds(aoi) +// .mode() +// .clip(aoi); +var tree_cover_fi_2 = tree_cover.get_tree_cover(aoi, year_fi_2); +tree_cover_fi_2 = tree_cover_fi_2.rename(['label_' + year_fi_2.slice(-2)]); + +var correction_in_0 = ee.ImageCollection(project_path+'/corrections_ccd_' + year_in_0) + .filterBounds(aoi) + .map(function(img) { + return img.select('cc_' + year_in_0).toInt32(); + }); + +correction_in_0 = ee.Algorithms.If( + correction_in_0.size().eq(0), + // Dummy masked image + ee.Image.constant(-9999) + .rename('cc_' + year_in_0) + .reproject('EPSG:4326', null, 25) + .clip(aoi) + .updateMask(ee.Image(1)), + // Real mean image + correction_in_0.mode().clip(aoi).select('cc_' + year_in_0) +); +correction_in_0 = ee.Image(correction_in_0); + +var correction_in_1 = ee.ImageCollection(project_path+'/corrections_ccd_' + year_in_1) + .filterBounds(aoi) + .map(function(img) { + return img.select('cc_' + year_in_1).toInt32(); + }); + +correction_in_1 = ee.Algorithms.If( + correction_in_1.size().eq(0), + // Dummy masked image + ee.Image.constant(-9999) + .rename('cc_' + year_in_1) + .reproject('EPSG:4326', null, 25) + .clip(aoi) + .updateMask(ee.Image(1)), + // Real mean image + correction_in_1.mode().clip(aoi).select('cc_' + year_in_1) +); +correction_in_1 = ee.Image(correction_in_1); + +var correction_in_2 = ee.ImageCollection(project_path+'/corrections_ccd_' + year_in_2) + .filterBounds(aoi) + .map(function(img) { + return img.select('cc_' + year_in_2).toInt32(); + }); + +correction_in_2 = ee.Algorithms.If( + correction_in_2.size().eq(0), + // Dummy masked image + ee.Image.constant(-9999) + .rename('cc_' + year_in_2) + .reproject('EPSG:4326', null, 25) + .clip(aoi) + .updateMask(ee.Image(1)), + // Real mean image + correction_in_2.mode().clip(aoi).select('cc_' + year_in_2) +); +correction_in_2 = ee.Image(correction_in_2); + +var correction_fi_0 = ee.ImageCollection(project_path+'/corrections_ccd_' + year_fi_0) + .filterBounds(aoi) + .map(function(img) { + return img.select('cc_' + year_fi_0).toInt32(); + }); + +correction_fi_0 = ee.Algorithms.If( + correction_fi_0.size().eq(0), + // Dummy masked image + ee.Image.constant(-9999) + .rename('cc_' + year_fi_0) + .reproject('EPSG:4326', null, 25) + .clip(aoi) + .updateMask(ee.Image(1)), + // Real mean image + correction_fi_0.mode().clip(aoi).select('cc_' + year_fi_0) +); +correction_fi_0 = ee.Image(correction_fi_0); + +var correction_fi_1 = ee.ImageCollection(project_path+'/corrections_ccd_' + year_fi_1) + .filterBounds(aoi) + .map(function(img) { + return img.select('cc_' + year_fi_1).toInt32(); + }); + +correction_fi_1 = ee.Algorithms.If( + correction_fi_1.size().eq(0), + // Dummy masked image + ee.Image.constant(-9999) + .rename('cc_' + year_fi_1) + .reproject('EPSG:4326', null, 25) + .clip(aoi) + .updateMask(ee.Image(1)), + // Real mean image + correction_fi_1.mode().clip(aoi).select('cc_' + year_fi_1) +); +correction_fi_1 = ee.Image(correction_fi_1); + +var correction_fi_2 = ee.ImageCollection(project_path+'/corrections_ccd_' + year_fi_2) + .filterBounds(aoi) + .map(function(img) { + return img.select('cc_' + year_fi_2).toInt32(); + }); + +correction_fi_2 = ee.Algorithms.If( + correction_fi_2.size().eq(0), + // Dummy masked image + ee.Image.constant(-9999) + .rename('cc_' + year_fi_2) + .reproject('EPSG:4326', null, 25) + .clip(aoi) + .updateMask(ee.Image(1)), + // Real mean image + correction_fi_2.mode().clip(aoi).select('cc_' + year_fi_2) +); +correction_fi_2 = ee.Image(correction_fi_2); + + +var palette2 =['FFA500', '007500', '000000']; +Map.addLayer(correction_fi_2, {bands:['cc_'+year_fi_2], min: 0, max: 2, palette: palette2}, 'Empty'); + +correction_in_0 = correction_in_0.rename(['cc_in_0']); +correction_in_1 = correction_in_1.rename(['cc_in_1']); +correction_in_2 = correction_in_2.rename(['cc_in_2']); +correction_fi_0 = correction_fi_0.rename(['cc_fi_0']); +correction_fi_1 = correction_fi_1.rename(['cc_fi_1']); +correction_fi_2 = correction_fi_2.rename(['cc_fi_2']); + + + + +ccd_in_0 = ccd_in_0.addBands(correction_in_0); +ccd_in_0 = ccd_in_0.unmask(-9999); +ccd_in_0 = ccd_in_0.expression( + "((b('cc_in_0')!=b('cc')) and (b('cc_in_0')!=-9999)) ? (b('cc_in_0'))"+ + ":b('cc')" + ).clip(aoi); +ccd_in_0 = ccd_in_0.updateMask(ccd_in_0.neq(-9999)); +ccd_in_0 = ccd_in_0.rename(['cc_in_0']); + + +ccd_in_1 = ccd_in_1.addBands(correction_in_1); +ccd_in_1 = ccd_in_1.unmask(-9999); +ccd_in_1 = ccd_in_1.expression( + "((b('cc_in_1')!=b('cc')) and (b('cc_in_1')!=-9999)) ? (b('cc_in_1'))"+ + ":b('cc')" + ).clip(aoi); +ccd_in_1 = ccd_in_1.updateMask(ccd_in_1.neq(-9999)); +ccd_in_1 = ccd_in_1.rename(['cc_in_1']); + + +ccd_in_2 = ccd_in_2.addBands(correction_in_2); +ccd_in_2 = ccd_in_2.unmask(-9999); +ccd_in_2 = ccd_in_2.expression( + "((b('cc_in_2')!=b('cc')) and (b('cc_in_2')!=-9999)) ? (b('cc_in_2'))"+ + ":b('cc')" + ).clip(aoi); +ccd_in_2 = ccd_in_2.updateMask(ccd_in_2.neq(-9999)); +ccd_in_2 = ccd_in_2.rename(['cc_in_2']); + + +ccd_fi_0 = ccd_fi_0.addBands(correction_fi_0); +ccd_fi_0 = ccd_fi_0.unmask(-9999); +ccd_fi_0 = ccd_fi_0.expression( + "((b('cc_fi_0')!=b('cc')) and (b('cc_fi_0')!=-9999)) ? (b('cc_fi_0'))"+ + ":b('cc')" + ).clip(aoi); +ccd_fi_0 = ccd_fi_0.updateMask(ccd_fi_0.neq(-9999)); +ccd_fi_0 = ccd_fi_0.rename(['cc_fi_0']); + + +ccd_fi_1 = ccd_fi_1.addBands(correction_fi_1); +ccd_fi_1 = ccd_fi_1.unmask(-9999); +ccd_fi_1 = ccd_fi_1.expression( + "((b('cc_fi_1')!=b('cc')) and (b('cc_fi_1')!=-9999)) ? (b('cc_fi_1'))"+ + ":b('cc')" + ).clip(aoi); +ccd_fi_1 = ccd_fi_1.updateMask(ccd_fi_1.neq(-9999)); +ccd_fi_1 = ccd_fi_1.rename(['cc_fi_1']); + + +ccd_fi_2 = ccd_fi_2.addBands(correction_fi_2); +ccd_fi_2 = ccd_fi_2.unmask(-9999); +ccd_fi_2 = ccd_fi_2.expression( + "((b('cc_fi_2')!=b('cc')) and (b('cc_fi_2')!=-9999)) ? (b('cc_fi_2'))"+ + ":b('cc')" + ).clip(aoi); +ccd_fi_2 = ccd_fi_2.updateMask(ccd_fi_2.neq(-9999)); +ccd_fi_2 = ccd_fi_2.rename(['cc_fi_2']); + + +tree_cover_in_0 = tree_cover_in_0.rename(['label_in_0']); +tree_cover_in_1 = tree_cover_in_1.rename(['label_in_1']); +tree_cover_in_2 = tree_cover_in_2.rename(['label_in_2']); +tree_cover_fi_0 = tree_cover_fi_0.rename(['label_fi_0']); +tree_cover_fi_1 = tree_cover_fi_1.rename(['label_fi_1']); +tree_cover_fi_2 = tree_cover_fi_2.rename(['label_fi_2']); + +var tree_cover_initial = tree_cover_in_0.addBands(tree_cover_in_1).addBands(tree_cover_in_2); +tree_cover_initial = tree_cover_initial.unmask(-9999); +tree_cover_initial = tree_cover_initial.expression( + "((b('label_in_1')!=-9999)) ? (b('label_in_1'))"+ + ":((b('label_in_0')!=-9999) and (b('label_in_1')==-9999) and (b('label_in_2')!=-9999)) ? (b('label_in_0'))"+ + ":(-9999)" + ).clip(aoi); +tree_cover_initial = tree_cover_initial.rename(['label']); +tree_cover_initial = tree_cover_initial.updateMask(tree_cover_initial.neq(-9999)); +// print(tree_cover_initial); + + +var tree_cover_final = tree_cover_fi_0.addBands(tree_cover_fi_1).addBands(tree_cover_fi_2); +tree_cover_final = tree_cover_final.unmask(-9999); +tree_cover_final = tree_cover_final.expression( + "((b('label_fi_1')!=-9999)) ? (b('label_fi_1'))"+ + ":((b('label_fi_0')!=-9999) and (b('label_fi_1')==-9999) and (b('label_fi_2')!=-9999)) ? (b('label_fi_0'))"+ + ":(-9999)" + ).clip(aoi); +tree_cover_final = tree_cover_final.rename(['label']); +tree_cover_final = tree_cover_final.updateMask(tree_cover_final.neq(-9999)); + +Map.addLayer(tree_cover_initial, {palette: ['white']}, 'tree cover initial'); +Map.addLayer(tree_cover_final, {palette: ['white']}, 'tree cover final'); + +// 2 denotes missing data in modal images + +var initial_image = ccd_in_0.addBands(ccd_in_1).addBands(ccd_in_2); +initial_image = initial_image.unmask(-9999); +// 0 denotes Low; 1 denotes High; 2 denotes missing data +initial_image = initial_image.expression( + "((b('cc_in_0')==0) and (b('cc_in_1')==0) and (b('cc_in_2')==0)) ? (0)"+ + ":((b('cc_in_0')==0) and (b('cc_in_1')==0) and (b('cc_in_2')==1)) ? (0)"+ + ":((b('cc_in_0')==0) and (b('cc_in_1')==1) and (b('cc_in_2')==0)) ? (0)"+ + ":((b('cc_in_0')==0) and (b('cc_in_1')==1) and (b('cc_in_2')==1)) ? (1)"+ + ":((b('cc_in_0')==1) and (b('cc_in_1')==0) and (b('cc_in_2')==0)) ? (0)"+ + ":((b('cc_in_0')==1) and (b('cc_in_1')==0) and (b('cc_in_2')==1)) ? (1)"+ + ":((b('cc_in_0')==1) and (b('cc_in_1')==1) and (b('cc_in_2')==0)) ? (1)"+ + ":((b('cc_in_0')==1) and (b('cc_in_1')==1) and (b('cc_in_2')==1)) ? (1)"+ + ":((b('cc_in_0')==-9999 or b('cc_in_2')==-9999) and (b('cc_in_1')!=-9999)) ? (b('cc_in_1'))"+ + ":((b('cc_in_0')!=-9999 and b('cc_in_1')==-9999 and b('cc_in_2')!=-9999) and (b('cc_in_0')==b('cc_in_2'))) ? (b('cc_in_0'))"+ + ":((b('cc_in_0')!=-9999 and b('cc_in_1')==-9999 and b('cc_in_2')==-9999)) ? (b('cc_in_0'))"+ + ":((b('cc_in_1')==-9999 and b('cc_in_2')!=-9999) and (b('cc_in_0')!=b('cc_in_2'))) ? (b('cc_in_2'))"+ + ":(-9999)" + ).clip(aoi); +initial_image = initial_image.rename(['cc']); +initial_image = initial_image.updateMask(initial_image.neq(-9999)); + +initial_image = initial_image.addBands(tree_cover_initial); +initial_image = initial_image.unmask(-9999); +// 2 denotes missing data +initial_image = initial_image.expression( + "((b('cc') == -9999) and (b('label') != -9999)) ? (2)"+ + ":b('cc')" + ).clip(aoi); +initial_image = initial_image.rename(['cc']); +initial_image = initial_image.updateMask(initial_image.neq(-9999)); +print("initial_image", initial_image) + +var final_image = ccd_fi_0.addBands(ccd_fi_1).addBands(ccd_fi_2); +final_image = final_image.unmask(-9999); +final_image = final_image.expression( + "((b('cc_fi_0')==0) and (b('cc_fi_1')==0) and (b('cc_fi_2')==0)) ? (0)"+ + ":((b('cc_fi_0')==0) and (b('cc_fi_1')==0) and (b('cc_fi_2')==1)) ? (0)"+ + ":((b('cc_fi_0')==0) and (b('cc_fi_1')==1) and (b('cc_fi_2')==0)) ? (0)"+ + ":((b('cc_fi_0')==0) and (b('cc_fi_1')==1) and (b('cc_fi_2')==1)) ? (1)"+ + ":((b('cc_fi_0')==1) and (b('cc_fi_1')==0) and (b('cc_fi_2')==0)) ? (0)"+ + ":((b('cc_fi_0')==1) and (b('cc_fi_1')==0) and (b('cc_fi_2')==1)) ? (1)"+ + ":((b('cc_fi_0')==1) and (b('cc_fi_1')==1) and (b('cc_fi_2')==0)) ? (1)"+ + ":((b('cc_fi_0')==1) and (b('cc_fi_1')==1) and (b('cc_fi_2')==1)) ? (1)"+ + ":((b('cc_fi_0')==-9999 or b('cc_fi_2')==-9999) and (b('cc_fi_1')!=-9999)) ? (b('cc_fi_1'))"+ + ":((b('cc_fi_0')!=-9999 and b('cc_fi_1')==-9999 and b('cc_fi_2')!=-9999) and (b('cc_fi_0')==b('cc_fi_2'))) ? (b('cc_fi_0'))"+ + ":((b('cc_fi_0')!=-9999 and b('cc_fi_1')==-9999 and b('cc_fi_2')==-9999)) ? (b('cc_fi_0'))"+ + ":((b('cc_fi_1')==-9999 and b('cc_fi_2')!=-9999) and (b('cc_fi_0')!=b('cc_fi_2'))) ? (b('cc_fi_2'))"+ + ":(-9999)" + ).clip(aoi); +final_image = final_image.rename(['cc']); +final_image = final_image.updateMask(final_image.neq(-9999)); + +final_image = final_image.addBands(tree_cover_final); +final_image = final_image.unmask(-9999); +// 2 denotes missing data +final_image = final_image.expression( + "((b('cc') == -9999) and (b('label') != -9999)) ? (2)"+ + ":b('cc')" + ).clip(aoi); +final_image = final_image.rename(['cc']); +final_image = final_image.updateMask(final_image.neq(-9999)); + +// var palette = ['orange', 'green', 'black']; +var palette =['FFA500', '007500', '000000']; +Map.addLayer(initial_image, {bands:['cc'], min: 0, max: 2, palette: palette}, 'initial modal image'); +Map.addLayer(final_image, {bands:['cc'], min: 0, max: 2, palette: palette}, 'final modal image'); + +// var change = initial_image.addBands(final_image); +// change = change.unmask(-9999); + +// print(change); + +// // 3 denotes missing data +// change = change.expression( +// "((b('cc')==0) and (b('cc_1')==0)) ? (0)"+ +// ":((b('cc')==1) and (b('cc_1')==1)) ? (0)"+ +// ":((b('cc')==0) and (b('cc_1')==1)) ? (1)"+ +// ":((b('cc')==1) and (b('cc_1')==0)) ? (-1)"+ +// ":((b('cc')==-9999) and (b('cc_1')!=-9999)) ? (2)"+ +// ":((b('cc')!=-9999) and (b('cc_1')==-9999)) ? (-2)"+ +// ":((b('cc')==2) or (b('cc_1')==2)) ? (3)"+ +// ":-9999" +// ) +// .clip(aoi); +// change = change.updateMask(change.neq(-9999)); +// // var palette = ['red', 'orange', 'white', 'lightgreen', 'darkgreen', 'black']; +// var palette = ['FF0000', 'FFA500', 'FFFFFF', '8AFF8A', '007500', '000000']; +// Map.addLayer(change, {min: -2, max: 3, palette: palette}, 'change layer'); + +// Export modal initial image +Export.image.toAsset({ + image: initial_image, + description: 'ccd_' + year_in_1 + '_' + agroclimaticZoneAcronymDict[acz], + assetId: project_path+'/modal_ccd_' + year_in_1 + '/' + agroclimaticZoneAcronymDict[acz], + region: aoi, + scale: 25, + crs: 'EPSG:4326', + maxPixels: 10000000000 + }); + +// Export modal final image +Export.image.toAsset({ + image: final_image, + description: 'ccd_' + year_fi_1 + '_' + agroclimaticZoneAcronymDict[acz], + assetId: project_path+'/modal_ccd_' + year_fi_1 + '/' + agroclimaticZoneAcronymDict[acz], + region: aoi, + scale: 25, + crs: 'EPSG:4326', + maxPixels: 10000000000 + }); + +// // Export CCD change image +// Export.image.toAsset({ +// image: change, +// description: 'ccd_change_' + agroclimaticZoneAcronymDict[acz], +// assetId: project_path+'/ccd_change_' + year_in_1 + '_' + year_fi_1 + '/' + agroclimaticZoneAcronymDict[acz], +// region: aoi, +// scale: 25, +// crs: 'EPSG:4326', +// maxPixels: 10000000000 +// }); + +//=========================================== +// var change_ccd = change; + +// for (var c=0; c year_1 and both must be of Integer type +var year_1 = 2023; +var year_2 = 2022; + +// var india_district_boundary = ee.FeatureCollection("projects/ee-indiasat/assets/india_district_boundaries"); +// var aoi = india_district_boundary.filter(ee.Filter.eq('Name', "Baloda Bazar")).geometry(); + +var india_boundary = ee.FeatureCollection("projects/ext-datasets/assets/datasets/ACZs"); + +var agroclimaticZoneAcronymDict = { + 'Eastern Plateau & Hills Region': 'EPAHR', + 'Southern Plateau and Hills Region': 'SPAHR', + 'East Coast Plains & Hills Region': 'ECPHR', + 'Western Plateau and Hills Region': 'WPAHR', + 'Central Plateau & Hills Region': 'CPAHR', + 'Lower Gangetic Plain Region': 'LGPR', + 'Middle Gangetic Plain Region': 'MGPR', + 'Upper Gangetic Plain Region': 'UGPR', + 'Trans Gangetic Plain Region': 'TGPR', + 'Eastern Himalayan Region': 'EHR', + 'Western Himalayan Region': 'WHR' +}; + +// var acz = 'Eastern Plateau & Hills Region'; +// var acz = 'Lower Gangetic Plain Region'; +// var acz = 'Western Himalayan Region'; +// var acz = 'Eastern Himalayan Region'; +// var acz = 'Upper Gangetic Plain Region'; +var acz = 'Middle Gangetic Plain Region'; +// var acz = 'Trans Gangetic Plain Region'; +// var acz = 'Central Plateau & Hills Region'; +// var acz = 'Western Plateau and Hills Region'; +// var acz = 'Southern Plateau and Hills Region'; +// var acz = 'East Coast Plains & Hills Region'; + +// Set the AOI +var aoi = india_boundary.filter(ee.Filter.eq('regionname', acz)).geometry(); + +// Set the change list according to the following: +// -2 -> Deforestation +// -1 -> Degradation +// 0 -> No Change +// 1 -> Improvement +// 2 -> Afforestation +// 3 -> Missing Data +var change_list = [-1, 1]; + +Map.addLayer(aoi, {'color': 'black'}, 'AOI'); +Map.centerObject(aoi, 7); + +var year_in_0 = String(year_1-1); +var year_in_1 = String(year_1); +var year_in_2 = String(year_1+1); + +var year_fi_0 = String(year_2-1); +var year_fi_1 = String(year_2); +var year_fi_2 = String(year_2+1); + +var project_path = 'projects/corestack1-dev-alpha/assets/tree_characteristics'; + +// var tree_cover_in_0 = ee.ImageCollection('projects/ee-mtpictd/assets/harsh/tree_cover_' + year_in_0) +// .filterBounds(aoi) +// .mode() +// .clip(aoi); +var tree_cover_in_0 = tree_cover.get_tree_cover(aoi, year_in_0); +tree_cover_in_0 = tree_cover_in_0.rename(['label_' + year_in_0.slice(-2)]); + + +// var tree_cover_in_1 = ee.ImageCollection('projects/ee-mtpictd/assets/harsh/tree_cover_' + year_in_1) +// .filterBounds(aoi) +// .mode() +// .clip(aoi); +var tree_cover_in_1 = tree_cover.get_tree_cover(aoi, year_in_1); +tree_cover_in_1 = tree_cover_in_1.rename(['label_' + year_in_1.slice(-2)]); + + +// var tree_cover_in_2 = ee.ImageCollection('projects/ee-mtpictd/assets/harsh/tree_cover_' + year_in_2) +// .filterBounds(aoi) +// .mode() +// .clip(aoi); +var tree_cover_in_2 = tree_cover.get_tree_cover(aoi, year_in_2); +tree_cover_in_2 = tree_cover_in_2.rename(['label_' + year_in_2.slice(-2)]); + + +// var tree_cover_fi_0 = ee.ImageCollection('projects/ee-mtpictd/assets/harsh/tree_cover_' + year_fi_0) +// .filterBounds(aoi) +// .mode() +// .clip(aoi); +var tree_cover_fi_0 = tree_cover.get_tree_cover(aoi, year_fi_0); +tree_cover_fi_0 = tree_cover_fi_0.rename(['label_' + year_fi_0.slice(-2)]); + + +// var tree_cover_fi_1 = ee.ImageCollection('projects/ee-mtpictd/assets/harsh/tree_cover_' + year_fi_1) +// .filterBounds(aoi) +// .mode() +// .clip(aoi); +var tree_cover_fi_1 = tree_cover.get_tree_cover(aoi, year_fi_1); +tree_cover_fi_1 = tree_cover_fi_1.rename(['label_' + year_fi_1.slice(-2)]); + + +// var tree_cover_fi_2 = ee.ImageCollection('projects/ee-mtpictd/assets/harsh/tree_cover_' + year_fi_2) +// .filterBounds(aoi) +// .mode() +// .clip(aoi); +var tree_cover_fi_2 = tree_cover.get_tree_cover(aoi, year_fi_2); +tree_cover_fi_2 = tree_cover_fi_2.rename(['label_' + year_fi_2.slice(-2)]); + +tree_cover_in_0 = tree_cover_in_0.rename(['label_in_0']); +tree_cover_in_1 = tree_cover_in_1.rename(['label_in_1']); +tree_cover_in_2 = tree_cover_in_2.rename(['label_in_2']); +tree_cover_fi_0 = tree_cover_fi_0.rename(['label_fi_0']); +tree_cover_fi_1 = tree_cover_fi_1.rename(['label_fi_1']); +tree_cover_fi_2 = tree_cover_fi_2.rename(['label_fi_2']); + +var tree_cover_initial = tree_cover_in_0.addBands(tree_cover_in_1).addBands(tree_cover_in_2); +tree_cover_initial = tree_cover_initial.unmask(-9999); +tree_cover_initial = tree_cover_initial.expression( + "((b('label_in_1')!=-9999)) ? (b('label_in_1'))"+ + ":((b('label_in_0')!=-9999) and (b('label_in_1')==-9999) and (b('label_in_2')!=-9999)) ? (b('label_in_0'))"+ + ":(-9999)" + ).clip(aoi); +tree_cover_initial = tree_cover_initial.rename(['label']); +tree_cover_initial = tree_cover_initial.updateMask(tree_cover_initial.neq(-9999)); +// print(tree_cover_initial); + + +var tree_cover_final = tree_cover_fi_0.addBands(tree_cover_fi_1).addBands(tree_cover_fi_2); +tree_cover_final = tree_cover_final.unmask(-9999); +tree_cover_final = tree_cover_final.expression( + "((b('label_fi_1')!=-9999)) ? (b('label_fi_1'))"+ + ":((b('label_fi_0')!=-9999) and (b('label_fi_1')==-9999) and (b('label_fi_2')!=-9999)) ? (b('label_fi_0'))"+ + ":(-9999)" + ).clip(aoi); +tree_cover_final = tree_cover_final.rename(['label']); +tree_cover_final = tree_cover_final.updateMask(tree_cover_final.neq(-9999)); + +Map.addLayer(tree_cover_initial, {palette: ['white']}, 'tree cover initial'); +Map.addLayer(tree_cover_final, {palette: ['white']}, 'tree cover final'); + + +// CH visualizations + +// var ch_in_0 = ee.ImageCollection(project_path+'/ch_' + year_in_0) +// .filterBounds(aoi) +// .mean() +// .clip(aoi); +var ch_in_0 = ee.ImageCollection(project_path+'/ch_' + year_in_0).filterBounds(aoi); + +ch_in_0 = ee.Algorithms.If( + ch_in_0.size().eq(0), + // Dummy masked image + ee.Image.constant([-9999, -9999, -9999, -9999]) + .rename(['ch_class','rh50_class','rh75_class','rh98_class']) + .reproject('EPSG:4326', null, 25) + .clip(aoi) + .updateMask(ee.Image(1)), + // Real mean image + ch_in_0.mean().clip(aoi) +); + +ch_in_0 = ee.Image(ch_in_0); + +// var ch_in_1 = ee.ImageCollection(project_path+'/ch_' + year_in_1) +// .filterBounds(aoi) +// .mean() +// .clip(aoi); +var ch_in_1 = ee.ImageCollection(project_path+'/ch_' + year_in_1).filterBounds(aoi); + +ch_in_1 = ee.Algorithms.If( + ch_in_1.size().eq(0), + // Dummy masked image + ee.Image.constant([-9999, -9999, -9999, -9999]) + .rename(['ch_class','rh50_class','rh75_class','rh98_class']) + .reproject('EPSG:4326', null, 25) + .clip(aoi) + .updateMask(ee.Image(1)), + // Real mean image + ch_in_1.mean().clip(aoi) +); + +ch_in_1 = ee.Image(ch_in_1); + +// var ch_in_2 = ee.ImageCollection(project_path+'/ch_' + year_in_2) +// .filterBounds(aoi) +// .mean() +// .clip(aoi); +var ch_in_2 = ee.ImageCollection(project_path+'/ch_' + year_in_2).filterBounds(aoi); + +ch_in_2 = ee.Algorithms.If( + ch_in_2.size().eq(0), + // Dummy masked image + ee.Image.constant([-9999, -9999, -9999, -9999]) + .rename(['ch_class','rh50_class','rh75_class','rh98_class']) + .reproject('EPSG:4326', null, 25) + .clip(aoi) + .updateMask(ee.Image(1)), + // Real mean image + ch_in_2.mean().clip(aoi) +); + +ch_in_2 = ee.Image(ch_in_2); + +// var ch_fi_0 = ee.ImageCollection(project_path+'/ch_' + year_fi_0) +// .filterBounds(aoi) +// .mean() +// .clip(aoi); +var ch_fi_0 = ee.ImageCollection(project_path+'/ch_' + year_fi_0).filterBounds(aoi); + +ch_fi_0 = ee.Algorithms.If( + ch_fi_0.size().eq(0), + // Dummy masked image + ee.Image.constant([-9999, -9999, -9999, -9999]) + .rename(['ch_class','rh50_class','rh75_class','rh98_class']) + .reproject('EPSG:4326', null, 25) + .clip(aoi) + .updateMask(ee.Image(1)), + // Real mean image + ch_fi_0.mean().clip(aoi) +); + +ch_fi_0 = ee.Image(ch_fi_0); + +// var ch_fi_1 = ee.ImageCollection(project_path+'/ch_' + year_fi_1) +// .filterBounds(aoi) +// .mean() +// .clip(aoi); +var ch_fi_1 = ee.ImageCollection(project_path+'/ch_' + year_fi_1).filterBounds(aoi); + +ch_fi_1 = ee.Algorithms.If( + ch_fi_1.size().eq(0), + // Dummy masked image + ee.Image.constant([-9999, -9999, -9999, -9999]) + .rename(['ch_class','rh50_class','rh75_class','rh98_class']) + .reproject('EPSG:4326', null, 25) + .clip(aoi) + .updateMask(ee.Image(1)), + // Real mean image + ch_fi_1.mean().clip(aoi) +); + +ch_fi_1 = ee.Image(ch_fi_1); + +// var ch_fi_2 = ee.ImageCollection(project_path+'/ch_' + year_fi_2) +// .filterBounds(aoi) +// .mean() +// .clip(aoi); +var ch_fi_2 = ee.ImageCollection(project_path+'/ch_' + year_fi_2).filterBounds(aoi); + +ch_fi_2 = ee.Algorithms.If( + ch_fi_2.size().eq(0), + // Dummy masked image + ee.Image.constant([-9999, -9999, -9999, -9999]) + .rename(['ch_class','rh50_class','rh75_class','rh98_class']) + .reproject('EPSG:4326', null, 25) + .clip(aoi) + .updateMask(ee.Image(1)), + // Real mean image + ch_fi_2.mean().clip(aoi) +); + +ch_fi_2 = ee.Image(ch_fi_2); + +// var correction_img = ee.ImageCollection('projects/ee-mtpictd/assets/harsh/corrections_ch') +// .filterBounds(aoi) +// .mode() +// .clip(aoi); + +// var correction_in_0 = ee.ImageCollection(project_path+'/corrections_ch_' + year_in_0) +// .filterBounds(aoi) +// .mode() +// .clip(aoi) +// .select(['ch_'+year_in_0, 'rh98_'+year_in_0, 'rh75_'+year_in_0, 'rh50_'+year_in_0]); + +var correction_in_0 = ee.ImageCollection(project_path+'/corrections_ch_' + year_in_0).filterBounds(aoi); + +correction_in_0 = ee.Algorithms.If( + correction_in_0.size().eq(0), + // Dummy masked image + ee.Image.constant([-9999, -9999, -9999, -9999]) + .rename(['ch_'+year_in_0, 'rh98_'+year_in_0, 'rh75_'+year_in_0, 'rh50_'+year_in_0]) + .reproject('EPSG:4326', null, 25) + .clip(aoi) + .updateMask(ee.Image(1)), + // Real mean image + correction_in_0.mode().clip(aoi).select(['ch_'+year_in_0, 'rh98_'+year_in_0, 'rh75_'+year_in_0, 'rh50_'+year_in_0]) +); +correction_in_0 = ee.Image(correction_in_0); + +// var correction_in_1 = ee.ImageCollection(project_path+'/corrections_ch_' + year_in_1) +// .filterBounds(aoi) +// .mode() +// .clip(aoi) +// .select(['ch_'+year_in_1, 'rh98_'+year_in_1, 'rh75_'+year_in_1, 'rh50_'+year_in_1]); +var correction_in_1 = ee.ImageCollection(project_path+'/corrections_ch_' + year_in_1).filterBounds(aoi); + +correction_in_1 = ee.Algorithms.If( + correction_in_1.size().eq(0), + // Dummy masked image + ee.Image.constant([-9999, -9999, -9999, -9999]) + .rename(['ch_'+year_in_1, 'rh98_'+year_in_1, 'rh75_'+year_in_1, 'rh50_'+year_in_1]) + .reproject('EPSG:4326', null, 25) + .clip(aoi) + .updateMask(ee.Image(1)), + // Real mean image + correction_in_1.mode().clip(aoi).select(['ch_'+year_in_1, 'rh98_'+year_in_1, 'rh75_'+year_in_1, 'rh50_'+year_in_1]) +); +correction_in_1 = ee.Image(correction_in_1); + +// var correction_in_2 = ee.ImageCollection(project_path+'/corrections_ch_' + year_in_2) +// .filterBounds(aoi) +// .mode() +// .clip(aoi) +// .select(['ch_'+year_in_2, 'rh98_'+year_in_2, 'rh75_'+year_in_2, 'rh50_'+year_in_2]); + +var correction_in_2 = ee.ImageCollection(project_path+'/corrections_ch_' + year_in_2).filterBounds(aoi); + +correction_in_2 = ee.Algorithms.If( + correction_in_2.size().eq(0), + // Dummy masked image + ee.Image.constant([-9999, -9999, -9999, -9999]) + .rename(['ch_'+year_in_2, 'rh98_'+year_in_2, 'rh75_'+year_in_2, 'rh50_'+year_in_2]) + .reproject('EPSG:4326', null, 25) + .clip(aoi) + .updateMask(ee.Image(1)), + // Real mean image + correction_in_2.mode().clip(aoi).select(['ch_'+year_in_2, 'rh98_'+year_in_2, 'rh75_'+year_in_2, 'rh50_'+year_in_2]) +); +correction_in_2 = ee.Image(correction_in_2); + +// var correction_fi_0 = ee.ImageCollection(project_path+'/corrections_ch_' + year_fi_0) +// .filterBounds(aoi) +// .mode() +// .clip(aoi) +// .select(['ch_'+year_fi_0, 'rh98_'+year_fi_0, 'rh75_'+year_fi_0, 'rh50_'+year_fi_0]); + +var correction_fi_0 = ee.ImageCollection(project_path+'/corrections_ch_' + year_fi_0).filterBounds(aoi); + +correction_fi_0 = ee.Algorithms.If( + correction_fi_0.size().eq(0), + // Dummy masked image + ee.Image.constant([-9999, -9999, -9999, -9999]) + .rename(['ch_'+year_fi_0, 'rh98_'+year_fi_0, 'rh75_'+year_fi_0, 'rh50_'+year_fi_0]) + .reproject('EPSG:4326', null, 25) + .clip(aoi) + .updateMask(ee.Image(1)), + // Real mean image + correction_fi_0.mode().clip(aoi).select(['ch_'+year_fi_0, 'rh98_'+year_fi_0, 'rh75_'+year_fi_0, 'rh50_'+year_fi_0]) +); +correction_fi_0 = ee.Image(correction_fi_0); + +// var correction_fi_1 = ee.ImageCollection(project_path+'/corrections_ch_' + year_fi_1) +// .filterBounds(aoi) +// .mode() +// .clip(aoi) +// .select(['ch_'+year_fi_1, 'rh98_'+year_fi_1, 'rh75_'+year_fi_1, 'rh50_'+year_fi_1]); + +var correction_fi_1 = ee.ImageCollection(project_path+'/corrections_ch_' + year_fi_1).filterBounds(aoi); + +correction_fi_1 = ee.Algorithms.If( + correction_fi_1.size().eq(0), + // Dummy masked image + ee.Image.constant([-9999, -9999, -9999, -9999]) + .rename(['ch_'+year_fi_1, 'rh98_'+year_fi_1, 'rh75_'+year_fi_1, 'rh50_'+year_fi_1]) + .reproject('EPSG:4326', null, 25) + .clip(aoi) + .updateMask(ee.Image(1)), + // Real mean image + correction_fi_1.mode().clip(aoi).select(['ch_'+year_fi_1, 'rh98_'+year_fi_1, 'rh75_'+year_fi_1, 'rh50_'+year_fi_1]) +); +correction_fi_1 = ee.Image(correction_fi_1); + +// var correction_fi_2 = ee.ImageCollection(project_path+'/corrections_ch_' + year_fi_2) +// .filterBounds(aoi) +// .mode() +// .clip(aoi) +// .select(['ch_'+year_fi_2, 'rh98_'+year_fi_2, 'rh75_'+year_fi_2, 'rh50_'+year_fi_2]); + +var correction_fi_2 = ee.ImageCollection(project_path+'/corrections_ch_' + year_fi_2).filterBounds(aoi); + +correction_fi_2 = ee.Algorithms.If( + correction_fi_2.size().eq(0), + // Dummy masked image + ee.Image.constant([-9999, -9999, -9999, -9999]) + .rename(['ch_'+year_fi_2, 'rh98_'+year_fi_2, 'rh75_'+year_fi_2, 'rh50_'+year_fi_2]) + .reproject('EPSG:4326', null, 25) + .clip(aoi) + .updateMask(ee.Image(1)), + // Real mean image + correction_fi_2.mode().clip(aoi).select(['ch_'+year_fi_2, 'rh98_'+year_fi_2, 'rh75_'+year_fi_2, 'rh50_'+year_fi_2]) +); +correction_fi_2 = ee.Image(correction_fi_2); + +// var correction_in_0 = correction_img.select(['ch_'+year_in_0, 'rh98_'+year_in_0, 'rh75_'+year_in_0, 'rh50_'+year_in_0]); +// var correction_in_1 = correction_img.select(['ch_'+year_in_1, 'rh98_'+year_in_1, 'rh75_'+year_in_1, 'rh50_'+year_in_1]); +// var correction_in_2 = correction_img.select(['ch_'+year_in_2, 'rh98_'+year_in_2, 'rh75_'+year_in_2, 'rh50_'+year_in_2]); +// var correction_fi_0 = correction_img.select(['ch_'+year_fi_0, 'rh98_'+year_fi_0, 'rh75_'+year_fi_0, 'rh50_'+year_fi_0]); +// var correction_fi_1 = correction_img.select(['ch_'+year_fi_1, 'rh98_'+year_fi_1, 'rh75_'+year_fi_1, 'rh50_'+year_fi_1]); +// var correction_fi_2 = correction_img.select(['ch_'+year_fi_2, 'rh98_'+year_fi_2, 'rh75_'+year_fi_2, 'rh50_'+year_fi_2]); + + +correction_in_0 = correction_in_0.rename(['ch_in_0', 'rh98_in_0', 'rh75_in_0', 'rh50_in_0']); +correction_in_1 = correction_in_1.rename(['ch_in_1', 'rh98_in_1', 'rh75_in_1', 'rh50_in_1']); +correction_in_2 = correction_in_2.rename(['ch_in_2', 'rh98_in_2', 'rh75_in_2', 'rh50_in_2']); +correction_fi_0 = correction_fi_0.rename(['ch_fi_0', 'rh98_fi_0', 'rh75_fi_0', 'rh50_fi_0']); +correction_fi_1 = correction_fi_1.rename(['ch_fi_1', 'rh98_fi_1', 'rh75_fi_1', 'rh50_fi_1']); +correction_fi_2 = correction_fi_2.rename(['ch_fi_2', 'rh98_fi_2', 'rh75_fi_2', 'rh50_fi_2']); + + +var ch_in_0_ch = ch_in_0.select('ch_class'); +ch_in_0_ch = ch_in_0_ch.addBands(correction_in_0); +ch_in_0_ch = ch_in_0_ch.unmask(-9999); +ch_in_0_ch = ch_in_0_ch.expression( + "((b('ch_in_0')!=b('ch_class')) and (b('ch_in_0')!=-9999)) ? (b('ch_in_0'))"+ + ":b('ch_class')" + ).clip(aoi); +ch_in_0_ch = ch_in_0_ch.updateMask(ch_in_0_ch.neq(-9999)); +ch_in_0_ch = ch_in_0_ch.rename(['ch_in_0_ch']); + + +var ch_in_0_rh98 = ch_in_0.select('rh98_class'); +ch_in_0_rh98 = ch_in_0_rh98.addBands(correction_in_0); +ch_in_0_rh98 = ch_in_0_rh98.unmask(-9999); +ch_in_0_rh98 = ch_in_0_rh98.expression( + "((b('rh98_in_0')!=b('rh98_class')) and (b('rh98_in_0')!=-9999)) ? (b('rh98_in_0'))"+ + ":b('rh98_class')" + ).clip(aoi); +ch_in_0_rh98 = ch_in_0_rh98.updateMask(ch_in_0_rh98.neq(-9999)); +ch_in_0_rh98 = ch_in_0_rh98.rename(['ch_in_0_rh98']); + +var ch_in_0_rh75 = ch_in_0.select('rh75_class'); +ch_in_0_rh75 = ch_in_0_rh75.addBands(correction_in_0); +ch_in_0_rh75 = ch_in_0_rh75.unmask(-9999); +ch_in_0_rh75 = ch_in_0_rh75.expression( + "((b('rh75_in_0')!=b('rh75_class')) and (b('rh75_in_0')!=-9999)) ? (b('rh75_in_0'))"+ + ":b('rh75_class')" + ).clip(aoi); +ch_in_0_rh75 = ch_in_0_rh75.updateMask(ch_in_0_rh75.neq(-9999)); +ch_in_0_rh75 = ch_in_0_rh75.rename(['ch_in_0_rh75']);ch_in_0_rh75 + + +var ch_in_0_rh50 = ch_in_0.select('rh50_class'); +ch_in_0_rh50 = ch_in_0_rh50.addBands(correction_in_0); +ch_in_0_rh50 = ch_in_0_rh50.unmask(-9999); +ch_in_0_rh50 = ch_in_0_rh50.expression( + "((b('rh50_in_0')!=b('rh50_class')) and (b('rh50_in_0')!=-9999)) ? (b('rh50_in_0'))"+ + ":b('rh50_class')" + ).clip(aoi); +ch_in_0_rh50 = ch_in_0_rh50.updateMask(ch_in_0_rh50.neq(-9999)); +ch_in_0_rh50 = ch_in_0_rh50.rename(['ch_in_0_rh50']); + + +ch_in_0 = ch_in_0_ch.addBands(ch_in_0_rh98).addBands(ch_in_0_rh75).addBands(ch_in_0_rh50); + + +var ch_in_1_ch = ch_in_1.select('ch_class'); +ch_in_1_ch = ch_in_1_ch.addBands(correction_in_1); +ch_in_1_ch = ch_in_1_ch.unmask(-9999); +ch_in_1_ch = ch_in_1_ch.expression( + "((b('ch_in_1')!=b('ch_class')) and (b('ch_in_1')!=-9999)) ? (b('ch_in_1'))"+ + ":b('ch_class')" + ).clip(aoi); +ch_in_1_ch = ch_in_1_ch.updateMask(ch_in_1_ch.neq(-9999)); +ch_in_1_ch = ch_in_1_ch.rename(['ch_in_1_ch']); + + +var ch_in_1_rh98 = ch_in_1.select('rh98_class'); +ch_in_1_rh98 = ch_in_1_rh98.addBands(correction_in_1); +ch_in_1_rh98 = ch_in_1_rh98.unmask(-9999); +ch_in_1_rh98 = ch_in_1_rh98.expression( + "((b('rh98_in_1')!=b('rh98_class')) and (b('rh98_in_1')!=-9999)) ? (b('rh98_in_1'))"+ + ":b('rh98_class')" + ).clip(aoi); +ch_in_1_rh98 = ch_in_1_rh98.updateMask(ch_in_1_rh98.neq(-9999)); +ch_in_1_rh98 = ch_in_1_rh98.rename(['ch_in_1_rh98']); + + +var ch_in_1_rh75 = ch_in_1.select('rh75_class'); +ch_in_1_rh75 = ch_in_1_rh75.addBands(correction_in_1); +ch_in_1_rh75 = ch_in_1_rh75.unmask(-9999); +ch_in_1_rh75 = ch_in_1_rh75.expression( + "((b('rh75_in_1')!=b('rh75_class')) and (b('rh75_in_1')!=-9999)) ? (b('rh75_in_1'))"+ + ":b('rh75_class')" + ).clip(aoi); +ch_in_1_rh75 = ch_in_1_rh75.updateMask(ch_in_1_rh75.neq(-9999)); +ch_in_1_rh75 = ch_in_1_rh75.rename(['ch_in_1_rh75']);ch_in_1_rh75 + + +var ch_in_1_rh50 = ch_in_1.select('rh50_class'); +ch_in_1_rh50 = ch_in_1_rh50.addBands(correction_in_1); +ch_in_1_rh50 = ch_in_1_rh50.unmask(-9999); +ch_in_1_rh50 = ch_in_1_rh50.expression( + "((b('rh50_in_1')!=b('rh50_class')) and (b('rh50_in_1')!=-9999)) ? (b('rh50_in_1'))"+ + ":b('rh50_class')" + ).clip(aoi); +ch_in_1_rh50 = ch_in_1_rh50.updateMask(ch_in_1_rh50.neq(-9999)); +ch_in_1_rh50 = ch_in_1_rh50.rename(['ch_in_1_rh50']); + + +ch_in_1 = ch_in_1_ch.addBands(ch_in_1_rh98).addBands(ch_in_1_rh75).addBands(ch_in_1_rh50); + +// print(ch_in_1); + +var ch_in_2_ch = ch_in_2.select('ch_class'); +ch_in_2_ch = ch_in_2_ch.addBands(correction_in_2); +ch_in_2_ch = ch_in_2_ch.unmask(-9999); +ch_in_2_ch = ch_in_2_ch.expression( + "((b('ch_in_2')!=b('ch_class')) and (b('ch_in_2')!=-9999)) ? (b('ch_in_2'))"+ + ":b('ch_class')" + ).clip(aoi); +ch_in_2_ch = ch_in_2_ch.updateMask(ch_in_2_ch.neq(-9999)); +ch_in_2_ch = ch_in_2_ch.rename(['ch_in_2_ch']); + + +var ch_in_2_rh98 = ch_in_2.select('rh98_class'); +ch_in_2_rh98 = ch_in_2_rh98.addBands(correction_in_2); +ch_in_2_rh98 = ch_in_2_rh98.unmask(-9999); +ch_in_2_rh98 = ch_in_2_rh98.expression( + "((b('rh98_in_2')!=b('rh98_class')) and (b('rh98_in_2')!=-9999)) ? (b('rh98_in_2'))"+ + ":b('rh98_class')" + ).clip(aoi); +ch_in_2_rh98 = ch_in_2_rh98.updateMask(ch_in_2_rh98.neq(-9999)); +ch_in_2_rh98 = ch_in_2_rh98.rename(['ch_in_2_rh98']); + + +var ch_in_2_rh75 = ch_in_2.select('rh75_class'); +ch_in_2_rh75 = ch_in_2_rh75.addBands(correction_in_2); +ch_in_2_rh75 = ch_in_2_rh75.unmask(-9999); +ch_in_2_rh75 = ch_in_2_rh75.expression( + "((b('rh75_in_2')!=b('rh75_class')) and (b('rh75_in_2')!=-9999)) ? (b('rh75_in_2'))"+ + ":b('rh75_class')" + ).clip(aoi); +ch_in_2_rh75 = ch_in_2_rh75.updateMask(ch_in_2_rh75.neq(-9999)); +ch_in_2_rh75 = ch_in_2_rh75.rename(['ch_in_2_rh75']);ch_in_2_rh75 + + +var ch_in_2_rh50 = ch_in_2.select('rh50_class'); +ch_in_2_rh50 = ch_in_2_rh50.addBands(correction_in_2); +ch_in_2_rh50 = ch_in_2_rh50.unmask(-9999); +ch_in_2_rh50 = ch_in_2_rh50.expression( + "((b('rh50_in_2')!=b('rh50_class')) and (b('rh50_in_2')!=-9999)) ? (b('rh50_in_2'))"+ + ":b('rh50_class')" + ).clip(aoi); +ch_in_2_rh50 = ch_in_2_rh50.updateMask(ch_in_2_rh50.neq(-9999)); +ch_in_2_rh50 = ch_in_2_rh50.rename(['ch_in_2_rh50']); + + +ch_in_2 = ch_in_2_ch.addBands(ch_in_2_rh98).addBands(ch_in_2_rh75).addBands(ch_in_2_rh50); + + +var ch_fi_0_ch = ch_fi_0.select('ch_class'); +ch_fi_0_ch = ch_fi_0_ch.addBands(correction_fi_0); +ch_fi_0_ch = ch_fi_0_ch.unmask(-9999); +ch_fi_0_ch = ch_fi_0_ch.expression( + "((b('ch_fi_0')!=b('ch_class')) and (b('ch_fi_0')!=-9999)) ? (b('ch_fi_0'))"+ + ":b('ch_class')" + ).clip(aoi); +ch_fi_0_ch = ch_fi_0_ch.updateMask(ch_fi_0_ch.neq(-9999)); +ch_fi_0_ch = ch_fi_0_ch.rename(['ch_fi_0_ch']); + + +var ch_fi_0_rh98 = ch_fi_0.select('rh98_class'); +ch_fi_0_rh98 = ch_fi_0_rh98.addBands(correction_fi_0); +ch_fi_0_rh98 = ch_fi_0_rh98.unmask(-9999); +ch_fi_0_rh98 = ch_fi_0_rh98.expression( + "((b('rh98_fi_0')!=b('rh98_class')) and (b('rh98_fi_0')!=-9999)) ? (b('rh98_fi_0'))"+ + ":b('rh98_class')" + ).clip(aoi); +ch_fi_0_rh98 = ch_fi_0_rh98.updateMask(ch_fi_0_rh98.neq(-9999)); +ch_fi_0_rh98 = ch_fi_0_rh98.rename(['ch_fi_0_rh98']); + + +var ch_fi_0_rh75 = ch_fi_0.select('rh75_class'); +ch_fi_0_rh75 = ch_fi_0_rh75.addBands(correction_fi_0); +ch_fi_0_rh75 = ch_fi_0_rh75.unmask(-9999); +ch_fi_0_rh75 = ch_fi_0_rh75.expression( + "((b('rh75_fi_0')!=b('rh75_class')) and (b('rh75_fi_0')!=-9999)) ? (b('rh75_fi_0'))"+ + ":b('rh75_class')" + ).clip(aoi); +ch_fi_0_rh75 = ch_fi_0_rh75.updateMask(ch_fi_0_rh75.neq(-9999)); +ch_fi_0_rh75 = ch_fi_0_rh75.rename(['ch_fi_0_rh75']);ch_fi_0_rh75 + + +var ch_fi_0_rh50 = ch_fi_0.select('rh50_class'); +ch_fi_0_rh50 = ch_fi_0_rh50.addBands(correction_fi_0); +ch_fi_0_rh50 = ch_fi_0_rh50.unmask(-9999); +ch_fi_0_rh50 = ch_fi_0_rh50.expression( + "((b('rh50_fi_0')!=b('rh50_class')) and (b('rh50_fi_0')!=-9999)) ? (b('rh50_fi_0'))"+ + ":b('rh50_class')" + ).clip(aoi); +ch_fi_0_rh50 = ch_fi_0_rh50.updateMask(ch_fi_0_rh50.neq(-9999)); +ch_fi_0_rh50 = ch_fi_0_rh50.rename(['ch_fi_0_rh50']); + + +ch_fi_0 = ch_fi_0_ch.addBands(ch_fi_0_rh98).addBands(ch_fi_0_rh75).addBands(ch_fi_0_rh50); + + +var ch_fi_1_ch = ch_fi_1.select('ch_class'); +ch_fi_1_ch = ch_fi_1_ch.addBands(correction_fi_1); +ch_fi_1_ch = ch_fi_1_ch.unmask(-9999); +ch_fi_1_ch = ch_fi_1_ch.expression( + "((b('ch_fi_1')!=b('ch_class')) and (b('ch_fi_1')!=-9999)) ? (b('ch_fi_1'))"+ + ":b('ch_class')" + ).clip(aoi); +ch_fi_1_ch = ch_fi_1_ch.updateMask(ch_fi_1_ch.neq(-9999)); +ch_fi_1_ch = ch_fi_1_ch.rename(['ch_fi_1_ch']); + + +var ch_fi_1_rh98 = ch_fi_1.select('rh98_class'); +ch_fi_1_rh98 = ch_fi_1_rh98.addBands(correction_fi_1); +ch_fi_1_rh98 = ch_fi_1_rh98.unmask(-9999); +ch_fi_1_rh98 = ch_fi_1_rh98.expression( + "((b('rh98_fi_1')!=b('rh98_class')) and (b('rh98_fi_1')!=-9999)) ? (b('rh98_fi_1'))"+ + ":b('rh98_class')" + ).clip(aoi); +ch_fi_1_rh98 = ch_fi_1_rh98.updateMask(ch_fi_1_rh98.neq(-9999)); +ch_fi_1_rh98 = ch_fi_1_rh98.rename(['ch_fi_1_rh98']); + + +var ch_fi_1_rh75 = ch_fi_1.select('rh75_class'); +ch_fi_1_rh75 = ch_fi_1_rh75.addBands(correction_fi_1); +ch_fi_1_rh75 = ch_fi_1_rh75.unmask(-9999); +ch_fi_1_rh75 = ch_fi_1_rh75.expression( + "((b('rh75_fi_1')!=b('rh75_class')) and (b('rh75_fi_1')!=-9999)) ? (b('rh75_fi_1'))"+ + ":b('rh75_class')" + ).clip(aoi); +ch_fi_1_rh75 = ch_fi_1_rh75.updateMask(ch_fi_1_rh75.neq(-9999)); +ch_fi_1_rh75 = ch_fi_1_rh75.rename(['ch_fi_1_rh75']);ch_fi_1_rh75 + + +var ch_fi_1_rh50 = ch_fi_1.select('rh50_class'); +ch_fi_1_rh50 = ch_fi_1_rh50.addBands(correction_fi_1); +ch_fi_1_rh50 = ch_fi_1_rh50.unmask(-9999); +ch_fi_1_rh50 = ch_fi_1_rh50.expression( + "((b('rh50_fi_1')!=b('rh50_class')) and (b('rh50_fi_1')!=-9999)) ? (b('rh50_fi_1'))"+ + ":b('rh50_class')" + ).clip(aoi); +ch_fi_1_rh50 = ch_fi_1_rh50.updateMask(ch_fi_1_rh50.neq(-9999)); +ch_fi_1_rh50 = ch_fi_1_rh50.rename(['ch_fi_1_rh50']); + + +ch_fi_1 = ch_fi_1_ch.addBands(ch_fi_1_rh98).addBands(ch_fi_1_rh75).addBands(ch_fi_1_rh50); + + +var ch_fi_2_ch = ch_fi_2.select('ch_class'); +ch_fi_2_ch = ch_fi_2_ch.addBands(correction_fi_2); +ch_fi_2_ch = ch_fi_2_ch.unmask(-9999); +ch_fi_2_ch = ch_fi_2_ch.expression( + "((b('ch_fi_2')!=b('ch_class')) and (b('ch_fi_2')!=-9999)) ? (b('ch_fi_2'))"+ + ":b('ch_class')" + ).clip(aoi); +ch_fi_2_ch = ch_fi_2_ch.updateMask(ch_fi_2_ch.neq(-9999)); +ch_fi_2_ch = ch_fi_2_ch.rename(['ch_fi_2_ch']); + + +var ch_fi_2_rh98 = ch_fi_2.select('rh98_class'); +ch_fi_2_rh98 = ch_fi_2_rh98.addBands(correction_fi_2); +ch_fi_2_rh98 = ch_fi_2_rh98.unmask(-9999); +ch_fi_2_rh98 = ch_fi_2_rh98.expression( + "((b('rh98_fi_2')!=b('rh98_class')) and (b('rh98_fi_2')!=-9999)) ? (b('rh98_fi_2'))"+ + ":b('rh98_class')" + ).clip(aoi); +ch_fi_2_rh98 = ch_fi_2_rh98.updateMask(ch_fi_2_rh98.neq(-9999)); +ch_fi_2_rh98 = ch_fi_2_rh98.rename(['ch_fi_2_rh98']); + + +var ch_fi_2_rh75 = ch_fi_2.select('rh75_class'); +ch_fi_2_rh75 = ch_fi_2_rh75.addBands(correction_fi_2); +ch_fi_2_rh75 = ch_fi_2_rh75.unmask(-9999); +ch_fi_2_rh75 = ch_fi_2_rh75.expression( + "((b('rh75_fi_2')!=b('rh75_class')) and (b('rh75_fi_2')!=-9999)) ? (b('rh75_fi_2'))"+ + ":b('rh75_class')" + ).clip(aoi); +ch_fi_2_rh75 = ch_fi_2_rh75.updateMask(ch_fi_2_rh75.neq(-9999)); +ch_fi_2_rh75 = ch_fi_2_rh75.rename(['ch_fi_2_rh75']);ch_fi_2_rh75 + + +var ch_fi_2_rh50 = ch_fi_2.select('rh50_class'); +ch_fi_2_rh50 = ch_fi_2_rh50.addBands(correction_fi_2); +ch_fi_2_rh50 = ch_fi_2_rh50.unmask(-9999); +ch_fi_2_rh50 = ch_fi_2_rh50.expression( + "((b('rh50_fi_2')!=b('rh50_class')) and (b('rh50_fi_2')!=-9999)) ? (b('rh50_fi_2'))"+ + ":b('rh50_class')" + ).clip(aoi); +ch_fi_2_rh50 = ch_fi_2_rh50.updateMask(ch_fi_2_rh50.neq(-9999)); +ch_fi_2_rh50 = ch_fi_2_rh50.rename(['ch_fi_2_rh50']); + + +ch_fi_2 = ch_fi_2_ch.addBands(ch_fi_2_rh98).addBands(ch_fi_2_rh75).addBands(ch_fi_2_rh50); + +var initial_image = ch_in_0.addBands(ch_in_1).addBands(ch_in_2); +// print(initial_image); + +var initial_image_ch = initial_image.select(['ch_in_0_ch', 'ch_in_1_ch', 'ch_in_2_ch']); +initial_image_ch = initial_image_ch.unmask(-9999); +initial_image_ch = initial_image_ch.expression( + "((b('ch_in_0_ch')==b('ch_in_2_ch')) and (b('ch_in_0_ch')!=-9999)) ? (b('ch_in_0_ch'))"+ + ":((b('ch_in_0_ch')==-9999 or b('ch_in_2_ch')==-9999) and (b('ch_in_1_ch')!=-9999)) ? (b('ch_in_1_ch'))"+ + ":((b('ch_in_0_ch')!=-9999 and b('ch_in_1_ch')==-9999 and b('ch_in_2_ch')==-9999)) ? (b('ch_in_0_ch'))"+ + ":((b('ch_in_1_ch')==-9999 and b('ch_in_2_ch')!=-9999) and (b('ch_in_0_ch')!=b('ch_in_2_ch'))) ? (b('ch_in_2_ch'))"+ + ":(b('ch_in_1_ch'))" + ).clip(aoi); +initial_image_ch = initial_image_ch.rename(['ch_class']); +initial_image_ch = initial_image_ch.updateMask(initial_image_ch.neq(-9999)); + +var initial_image_rh98 = initial_image.select(['ch_in_0_rh98', 'ch_in_1_rh98', 'ch_in_2_rh98']); +initial_image_rh98 = initial_image_rh98.unmask(-9999); +initial_image_rh98 = initial_image_rh98.expression( + "((b('ch_in_0_rh98')==b('ch_in_2_rh98')) and (b('ch_in_0_rh98')!=-9999)) ? (b('ch_in_0_rh98'))"+ + ":((b('ch_in_0_rh98')==-9999 or b('ch_in_2_rh98')==-9999) and (b('ch_in_1_rh98')!=-9999)) ? (b('ch_in_1_rh98'))"+ + ":((b('ch_in_0_rh98')!=-9999 and b('ch_in_1_rh98')==-9999 and b('ch_in_2_rh98')==-9999)) ? (b('ch_in_0_rh98'))"+ + ":((b('ch_in_1_rh98')==-9999 and b('ch_in_2_rh98')!=-9999) and (b('ch_in_0_rh98')!=b('ch_in_2_rh98'))) ? (b('ch_in_2_rh98'))"+ + ":(b('ch_in_1_rh98'))" + ).clip(aoi); +initial_image_rh98 = initial_image_rh98.rename(['rh98_class']); +initial_image_rh98 = initial_image_rh98.updateMask(initial_image_rh98.neq(-9999)); + +var initial_image_rh75 = initial_image.select(['ch_in_0_rh75', 'ch_in_1_rh75', 'ch_in_2_rh75']); +initial_image_rh75 = initial_image_rh75.unmask(-9999); +initial_image_rh75 = initial_image_rh75.expression( + "((b('ch_in_0_rh75')==b('ch_in_2_rh75')) and (b('ch_in_0_rh75')!=-9999)) ? (b('ch_in_0_rh75'))"+ + ":((b('ch_in_0_rh75')==-9999 or b('ch_in_2_rh75')==-9999) and (b('ch_in_1_rh75')!=-9999)) ? (b('ch_in_1_rh75'))"+ + ":((b('ch_in_0_rh75')!=-9999 and b('ch_in_1_rh75')==-9999 and b('ch_in_2_rh75')==-9999)) ? (b('ch_in_0_rh75'))"+ + ":((b('ch_in_1_rh75')==-9999 and b('ch_in_2_rh75')!=-9999) and (b('ch_in_0_rh75')!=b('ch_in_2_rh75'))) ? (b('ch_in_2_rh75'))"+ + ":(b('ch_in_1_rh75'))" + ).clip(aoi); +initial_image_rh75 = initial_image_rh75.rename(['rh75_class']); +initial_image_rh75 = initial_image_rh75.updateMask(initial_image_rh75.neq(-9999)); + +var initial_image_rh50 = initial_image.select(['ch_in_0_rh50', 'ch_in_1_rh50', 'ch_in_2_rh50']); +initial_image_rh50 = initial_image_rh50.unmask(-9999); +initial_image_rh50 = initial_image_rh50.expression( + "((b('ch_in_0_rh50')==b('ch_in_2_rh50')) and (b('ch_in_0_rh50')!=-9999)) ? (b('ch_in_0_rh50'))"+ + ":((b('ch_in_0_rh50')==-9999 or b('ch_in_2_rh50')==-9999) and (b('ch_in_1_rh50')!=-9999)) ? (b('ch_in_1_rh50'))"+ + ":((b('ch_in_0_rh50')!=-9999 and b('ch_in_1_rh50')==-9999 and b('ch_in_2_rh50')==-9999)) ? (b('ch_in_0_rh50'))"+ + ":((b('ch_in_1_rh50')==-9999 and b('ch_in_2_rh50')!=-9999) and (b('ch_in_0_rh50')!=b('ch_in_2_rh50'))) ? (b('ch_in_2_rh50'))"+ + ":(b('ch_in_1_rh50'))" + ).clip(aoi); +initial_image_rh50 = initial_image_rh50.rename(['rh50_class']); +initial_image_rh50 = initial_image_rh50.updateMask(initial_image_rh50.neq(-9999)); + +initial_image_ch = initial_image_ch.addBands(tree_cover_initial); +initial_image_ch = initial_image_ch.unmask(-9999); + +// 4 denotes missing data +initial_image_ch = initial_image_ch.expression( + "((b('ch_class') == -9999) and (b('label') != -9999)) ? (4)"+ + ":b('ch_class')" + ).clip(aoi); +initial_image_ch = initial_image_ch.rename(['ch_class']); +initial_image_ch = initial_image_ch.updateMask(initial_image_ch.neq(-9999)); + +initial_image = initial_image_ch.addBands(initial_image_rh98).addBands(initial_image_rh75) + .addBands(initial_image_rh50); + + +var final_image = ch_fi_0.addBands(ch_fi_1).addBands(ch_fi_2); + +var final_image_ch = final_image.select(['ch_fi_0_ch', 'ch_fi_1_ch', 'ch_fi_2_ch']); +final_image_ch = final_image_ch.unmask(-9999); +final_image_ch = final_image_ch.expression( + "((b('ch_fi_0_ch')==b('ch_fi_2_ch')) and (b('ch_fi_0_ch')!=-9999)) ? (b('ch_fi_0_ch'))"+ + ":((b('ch_fi_0_ch')==-9999 or b('ch_fi_2_ch')==-9999) and (b('ch_fi_1_ch')!=-9999)) ? (b('ch_fi_1_ch'))"+ + ":((b('ch_fi_0_ch')!=-9999 and b('ch_fi_1_ch')==-9999 and b('ch_fi_2_ch')==-9999)) ? (b('ch_fi_0_ch'))"+ + ":((b('ch_fi_1_ch')==-9999 and b('ch_fi_2_ch')!=-9999) and (b('ch_fi_0_ch')!=b('ch_fi_2_ch'))) ? (b('ch_fi_2_ch'))"+ + ":(b('ch_fi_1_ch'))" + ).clip(aoi); +final_image_ch = final_image_ch.rename(['ch_class']); +final_image_ch = final_image_ch.updateMask(final_image_ch.neq(-9999)); + +var final_image_rh98 = final_image.select(['ch_fi_0_rh98', 'ch_fi_1_rh98', 'ch_fi_2_rh98']); +final_image_rh98 = final_image_rh98.unmask(-9999); +final_image_rh98 = final_image_rh98.expression( + "((b('ch_fi_0_rh98')==b('ch_fi_2_rh98')) and (b('ch_fi_0_rh98')!=-9999)) ? (b('ch_fi_0_rh98'))"+ + ":((b('ch_fi_0_rh98')==-9999 or b('ch_fi_2_rh98')==-9999) and (b('ch_fi_1_rh98')!=-9999)) ? (b('ch_fi_1_rh98'))"+ + ":((b('ch_fi_0_rh98')!=-9999 and b('ch_fi_1_rh98')==-9999 and b('ch_fi_2_rh98')==-9999)) ? (b('ch_fi_0_rh98'))"+ + ":((b('ch_fi_1_rh98')==-9999 and b('ch_fi_2_rh98')!=-9999) and (b('ch_fi_0_rh98')!=b('ch_fi_2_rh98'))) ? (b('ch_fi_2_rh98'))"+ + ":(b('ch_fi_1_rh98'))" + ).clip(aoi); +final_image_rh98 = final_image_rh98.rename(['rh98_class']); +final_image_rh98 = final_image_rh98.updateMask(final_image_rh98.neq(-9999)); + +var final_image_rh75 = final_image.select(['ch_fi_0_rh75', 'ch_fi_1_rh75', 'ch_fi_2_rh75']); +final_image_rh75 = final_image_rh75.unmask(-9999); +final_image_rh75 = final_image_rh75.expression( + "((b('ch_fi_0_rh75')==b('ch_fi_2_rh75')) and (b('ch_fi_0_rh75')!=-9999)) ? (b('ch_fi_0_rh75'))"+ + ":((b('ch_fi_0_rh75')==-9999 or b('ch_fi_2_rh75')==-9999) and (b('ch_fi_1_rh75')!=-9999)) ? (b('ch_fi_1_rh75'))"+ + ":((b('ch_fi_0_rh75')!=-9999 and b('ch_fi_1_rh75')==-9999 and b('ch_fi_2_rh75')==-9999)) ? (b('ch_fi_0_rh75'))"+ + ":((b('ch_fi_1_rh75')==-9999 and b('ch_fi_2_rh75')!=-9999) and (b('ch_fi_0_rh75')!=b('ch_fi_2_rh75'))) ? (b('ch_fi_2_rh75'))"+ + ":(b('ch_fi_1_rh75'))" + ).clip(aoi); +final_image_rh75 = final_image_rh75.rename(['rh75_class']); +final_image_rh75 = final_image_rh75.updateMask(final_image_rh75.neq(-9999)); + +var final_image_rh50 = final_image.select(['ch_fi_0_rh50', 'ch_fi_1_rh50', 'ch_fi_2_rh50']); +final_image_rh50 = final_image_rh50.unmask(-9999); +final_image_rh50 = final_image_rh50.expression( + "((b('ch_fi_0_rh50')==b('ch_fi_2_rh50')) and (b('ch_fi_0_rh50')!=-9999)) ? (b('ch_fi_0_rh50'))"+ + ":((b('ch_fi_0_rh50')==-9999 or b('ch_fi_2_rh50')==-9999) and (b('ch_fi_1_rh50')!=-9999)) ? (b('ch_fi_1_rh50'))"+ + ":((b('ch_fi_0_rh50')!=-9999 and b('ch_fi_1_rh50')==-9999 and b('ch_fi_2_rh50')==-9999)) ? (b('ch_fi_0_rh50'))"+ + ":((b('ch_fi_1_rh50')==-9999 and b('ch_fi_2_rh50')!=-9999) and (b('ch_fi_0_rh50')!=b('ch_fi_2_rh50'))) ? (b('ch_fi_2_rh50'))"+ + ":(b('ch_fi_1_rh50'))" + ).clip(aoi); +final_image_rh50 = final_image_rh50.rename(['rh50_class']); +final_image_rh50 = final_image_rh50.updateMask(final_image_rh50.neq(-9999)); + +final_image_ch = final_image_ch.addBands(tree_cover_final); +final_image_ch = final_image_ch.unmask(-9999); +// 4 denotes missing data +final_image_ch = final_image_ch.expression( + "((b('ch_class') == -9999) and (b('label') != -9999)) ? (4)"+ + ":b('ch_class')" + ).clip(aoi); +final_image_ch = final_image_ch.rename(['ch_class']); +final_image_ch = final_image_ch.updateMask(final_image_ch.neq(-9999)); + +final_image = final_image_ch.addBands(final_image_rh98).addBands(final_image_rh75) + .addBands(final_image_rh50); + + +var palette =['FFA500', 'DEE64C', 'DEE64C','007500', '000000']; +Map.addLayer(initial_image, {bands:['ch_class'], min: 0, max: 4, palette: palette}, 'initial modal image ch'); +Map.addLayer(final_image, {bands:['ch_class'], min: 0, max: 4, palette: palette}, 'final modal image ch'); + +initial_image = initial_image.unmask(-9999); +initial_image = initial_image.expression( + "((b('ch_class') == 4)) ? (8)"+ + ": ((b('rh50_class') == 0) and (b('rh75_class') == 0) and (b('rh98_class') == 0)) ? (0)"+ + ": ((b('rh50_class') == 0) and (b('rh75_class') == 0) and (b('rh98_class') == 1)) ? (1)"+ + ": ((b('rh50_class') == 0) and (b('rh75_class') == 1) and (b('rh98_class') == 0)) ? (2)"+ + ": ((b('rh50_class') == 0) and (b('rh75_class') == 1) and (b('rh98_class') == 1)) ? (3)"+ + ": ((b('rh50_class') == 1) and (b('rh75_class') == 0) and (b('rh98_class') == 0)) ? (4)"+ + ": ((b('rh50_class') == 1) and (b('rh75_class') == 0) and (b('rh98_class') == 1)) ? (5)"+ + ": ((b('rh50_class') == 1) and (b('rh75_class') == 1) and (b('rh98_class') == 0)) ? (6)"+ + ": ((b('rh50_class') == 1) and (b('rh75_class') == 1) and (b('rh98_class') == 1)) ? (7)"+ + ": ((b('rh50_class') != -9999) or (b('rh75_class') != -9999) and (b('rh98_class') != -9999)) ? (8)"+ + ":-9999" + ).clip(aoi); +initial_image = initial_image.updateMask(initial_image.neq(-9999)); +initial_image = initial_image.rename(['ch_class']); + + +final_image = final_image.unmask(-9999); +final_image = final_image.expression( + "((b('ch_class') == 4)) ? (8)"+ + ": ((b('rh50_class') == 0) and (b('rh75_class') == 0) and (b('rh98_class') == 0)) ? (0)"+ + ": ((b('rh50_class') == 0) and (b('rh75_class') == 0) and (b('rh98_class') == 1)) ? (1)"+ + ": ((b('rh50_class') == 0) and (b('rh75_class') == 1) and (b('rh98_class') == 0)) ? (2)"+ + ": ((b('rh50_class') == 0) and (b('rh75_class') == 1) and (b('rh98_class') == 1)) ? (3)"+ + ": ((b('rh50_class') == 1) and (b('rh75_class') == 0) and (b('rh98_class') == 0)) ? (4)"+ + ": ((b('rh50_class') == 1) and (b('rh75_class') == 0) and (b('rh98_class') == 1)) ? (5)"+ + ": ((b('rh50_class') == 1) and (b('rh75_class') == 1) and (b('rh98_class') == 0)) ? (6)"+ + ": ((b('rh50_class') == 1) and (b('rh75_class') == 1) and (b('rh98_class') == 1)) ? (7)"+ + ": ((b('rh50_class') != -9999) or (b('rh75_class') != -9999) and (b('rh98_class') != -9999)) ? (8)"+ + ":-9999" + ).clip(aoi); +final_image = final_image.updateMask(final_image.neq(-9999)); +final_image = final_image.rename(['ch_class']); + +var palette =['FFA500', 'FFA500', 'DEE64C', 'DEE64C', 'DEE64C', 'DEE64C', '007500', '007500', '000000']; +Map.addLayer(initial_image, {bands:['ch_class'], min: 0, max: 8, palette: palette}, 'initial modal image ch'); +Map.addLayer(final_image, {bands:['ch_class'], min: 0, max: 8, palette: palette}, 'final modal image ch'); + + +var change = initial_image.addBands(final_image); +change = change.unmask(-9999); +// 3 denotes missing data +change = change.expression( + "((b('ch_class') == -9999) and (b('ch_class_1') != -9999)) ? (2)" + + ": ((b('ch_class') != -9999) and (b('ch_class_1') == -9999)) ? (-2)"+ + ": ((b('ch_class') == 8) or (b('ch_class_1') == 8)) ? (3)" + + ": ((b('ch_class') == b('ch_class_1')) and (b('ch_class') != -9999)) ? (0)" + + ": ((b('ch_class') < b('ch_class_1')) and (b('ch_class') != -9999) and ((b('ch_class') <= 1) or (b('ch_class') >= 4))) ? (1)" + + ": ((b('ch_class') > b('ch_class_1')) and (b('ch_class_1') != -9999)) ? (-1)" + + ": ((b('ch_class') == 2) and ((b('ch_class_1') == 3) or (b('ch_class_1') == 6) or (b('ch_class_1') == 7))) ? (1)" + + ": ((b('ch_class') == 3) and ((b('ch_class_1') == 6) or (b('ch_class_1') == 7))) ? (1)" + + ": (((b('ch_class') == 2) or (b('ch_class') == 3)) and ((b('ch_class_1') == 4) or (b('ch_class_1') == 5))) ? (-1)"+ + ":-9999" +).clip(aoi); +change = change.updateMask(change.neq(-9999)); +// var palette = ['red', 'orange', 'white', 'lightgreen', 'green', 'black']; +var palette = ['FF0000', 'FFA500', 'FFFFFF', '8AFF8A', '007500', '000000']; +Map.addLayer(change, {min: -2, max: 3, palette: palette}, 'change layer ch'); + +// Export modal initial image +Export.image.toAsset({ + image: initial_image, + description: 'ch_' + year_in_1 + '_' + agroclimaticZoneAcronymDict[acz], + assetId: project_path+'/modal_ch_' + year_in_1 + '/' + agroclimaticZoneAcronymDict[acz], + region: aoi, + scale: 25, + crs: 'EPSG:4326', + maxPixels: 10000000000 + }); + +// Export modal final image +Export.image.toAsset({ + image: final_image, + description: 'ch_' + year_fi_1 + '_' + agroclimaticZoneAcronymDict[acz], + assetId: project_path+'/modal_ch_' + year_fi_1 + '/' + agroclimaticZoneAcronymDict[acz], + region: aoi, + scale: 25, + crs: 'EPSG:4326', + maxPixels: 10000000000 + }); + +// // Export CH change image +// Export.image.toAsset({ +// image: change, +// description: 'ch_change_' + agroclimaticZoneAcronymDict[acz], +// assetId: project_path+'/ch_change_' + year_in_1 + '_' + year_fi_1 + '/' + agroclimaticZoneAcronymDict[acz], +// region: aoi, +// scale: 25, +// crs: 'EPSG:4326', +// maxPixels: 10000000000 +// }); + +// Quantifying CH change classes statistics +// var change_chm = change; + +// for (var c=0; c year_1 and both must be of Integer type +var year_1 = 2017; +var year_2 = 2023; + +var india_boundary = ee.FeatureCollection("projects/ext-datasets/assets/datasets/ACZs"); + +var agroclimaticZoneAcronymDict = { + 'Eastern Plateau & Hills Region': 'EPAHR', + 'Southern Plateau and Hills Region': 'SPAHR', + 'East Coast Plains & Hills Region': 'ECPHR', + 'Western Plateau and Hills Region': 'WPAHR', + 'Central Plateau & Hills Region': 'CPAHR', + 'Lower Gangetic Plain Region': 'LGPR', + 'Middle Gangetic Plain Region': 'MGPR', + 'Upper Gangetic Plain Region': 'UGPR', + 'Trans Gangetic Plain Region': 'TGPR', + 'Eastern Himalayan Region': 'EHR', + 'Western Himalayan Region': 'WHR' +}; + +var acz = 'Eastern Plateau & Hills Region'; +// var acz = 'Lower Gangetic Plain Region'; +// var acz = 'Western Himalayan Region'; +// var acz = 'Eastern Himalayan Region'; +// var acz = 'Upper Gangetic Plain Region'; +// var acz = 'Middle Gangetic Plain Region'; +// var acz = 'Trans Gangetic Plain Region'; +// var acz = 'Central Plateau & Hills Region'; +// var acz = 'Western Plateau and Hills Region'; +// var acz = 'Southern Plateau and Hills Region'; +// var acz = 'East Coast Plains & Hills Region'; + +// var fc = ee.FeatureCollection('users/mtpictd/world_boundary'); +// fc = fc.filter(ee.Filter.eq('Name', 'India')); +// var aoi = fc.geometry(); + +var aoi = india_boundary.filter(ee.Filter.eq('regionname', acz)).geometry(); +var project_path = 'projects/corestack-trees/assets/tree_characteristics'; + +var year_in_1 = String(year_1); +var year_fi_1 = String(year_2); + + +var change_ccd = ee.ImageCollection(project_path+'/ccd_change_' + year_in_1 + '_' + year_fi_1) + .filterBounds(aoi) + .mean() + .clip(aoi); + +var change_chm = ee.ImageCollection(project_path+'/ch_change_' + year_in_1 + '_' + year_fi_1) + .filterBounds(aoi) + .mean() + .clip(aoi); + + +var palette = ['red', 'orange', 'white', 'lightgreen', 'green', 'black']; +Map.addLayer(change_ccd, {min: -2, max: 3, palette: palette}, 'ccd change'); +Map.addLayer(change_chm, {min: -2, max: 3, palette: palette}, 'ch change'); + + +var overall_change = change_ccd.addBands(change_chm); + +overall_change = overall_change.unmask(-9999); +overall_change = overall_change.expression( + "((b('constant') == 3) or (b('constant_1') == 3)) ? (5)"+ + ": ((b('constant') == -2) or (b('constant') == 2)) ? (b('constant'))"+ + ": ((b('constant') == 1) and (b('constant_1') == 1)) ? (1)"+ + ": ((b('constant') == 1) and (b('constant_1') == -1)) ? (3)"+ + ": ((b('constant') == 1) and (b('constant_1') == 0)) ? (1)"+ + ": ((b('constant') == -1) and (b('constant_1') == 1)) ? (4)"+ + ": ((b('constant') == -1) and (b('constant_1') == -1)) ? (-1)"+ + ": ((b('constant') == -1) and (b('constant_1') == 0)) ? (-1)"+ + ": ((b('constant') == 0) and (b('constant_1') == 1)) ? (1)"+ + ": ((b('constant') == 0) and (b('constant_1') == -1)) ? (-1)"+ + ": ((b('constant') == 0) and (b('constant_1') == 0)) ? (0)"+ + ": -9999" +).clip(aoi); +overall_change = overall_change.updateMask(overall_change.neq(-9999)); +// print(overall_change); +var palette = ['FF0000', 'FFA500', 'FFFFFF', '8AFF8A', '007500', 'DEE64C', 'DEE64C', '000000']; +Map.addLayer(overall_change, {min: -2, max: 5, palette: palette}, 'overall change'); + +Export.image.toAsset({ + image: overall_change, + description: 'overall_change_' + agroclimaticZoneAcronymDict[acz], + assetId: project_path+'/overall_change_' + year_in_1 + '_' + year_fi_1 + '/' + agroclimaticZoneAcronymDict[acz], + region: aoi, + scale: 25, + crs: 'EPSG:4326', + maxPixels: 10000000000 + }); + + +// // Set the change list according to the following: +// // -2 -> Deforestation +// // -1 -> Degradation +// // 0 -> No Change +// // 1 -> Improvement +// // 2 -> Afforestation +// // 3 denotes partial improvement and degradation where ccd improves and ch degrades +// // 4 denotes partial improvement and degradation where ccd degrades and ch improves +// // 5 -> Missing Data + +// // var change_list = [2, -2, 1, -1, 3, 4, 0, 5]; +// var change_list = [-1, 3, 4]; + +// for (var c=0; c