• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

akvo / drought-map-hub / 17996263895

25 Sep 2025 03:29AM UTC coverage: 91.664% (+0.1%) from 91.567%
17996263895

push

github

ifirmawan
[#8] FE: enhance CompletePage with improved messaging and layout for setup completion

208 of 243 branches covered (85.6%)

Branch coverage included in aggregate %.

2277 of 2468 relevant lines covered (92.26%)

0.92 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

76.06
/app/backend/utils/geojson_processor.py
1
import json
1✔
2
from pathlib import Path
1✔
3
from django.conf import settings
1✔
4
import geopandas as gpd
1✔
5
from topojson import Topology
1✔
6

7

8
def process_geojson_file(
1✔
9
    geojson_file,
10
    map_name_key=None,
11
):
12
    """
13
    Process uploaded GeoJSON file and convert it to TopoJSON.
14
    Adds 'administration_id' property to features if it doesn't exist.
15
    Saves to country.topojson in production or country-test.topojson
16
    during tests.
17
    Args:
18
        geojson_file: Django UploadedFile object containing GeoJSON data
19
    Returns:
20
        str: Path to the saved TopoJSON file
21
    Raises:
22
        ValueError: If the file cannot be processed or converted
23
    """
24
    try:
1✔
25
        # Read the GeoJSON content
26
        geojson_content = geojson_file.read()
1✔
27
        geojson_data = json.loads(geojson_content.decode('utf-8'))
1✔
28
        # Reset file pointer for potential reuse
29
        geojson_file.seek(0)
1✔
30
        # Add administration_id to features if it doesn't exist
31
        if 'features' in geojson_data:
1✔
32
            for i, feature in enumerate(geojson_data['features']):
1✔
33
                # Ensure properties exist
34
                if 'properties' not in feature:
1✔
35
                    feature['properties'] = {}
×
36
                feature['properties']['administration_id'] = i + 1
1✔
37
                if 'name' not in feature['properties']:
1✔
38
                    feature['properties']['name'] = f"Administration #{i + 1}"
×
39
                if map_name_key in feature['properties']:
1✔
40
                    feature['properties']['name'] = (
1✔
41
                        feature['properties'][map_name_key]
42
                    )
43
        # Create a GeoDataFrame from the GeoJSON data
44
        gdf = gpd.GeoDataFrame.from_features(
1✔
45
            geojson_data.get('features', []),
46
            crs="EPSG:4326"
47
        )
48
        # Convert the GeoDataFrame to a TopoJSON topology
49
        topo = Topology(gdf)
1✔
50
        # Get the TopoJSON. `Topology.to_json()` may return either a
51
        # JSON string or a dict.
52
        topojson_raw = topo.to_json()
1✔
53
        # If it's a string, parse it to a Python dict to avoid writing a
54
        # quoted JSON string into the .topojson file (which would add
55
        # extra quotes).
56
        if isinstance(topojson_raw, str):
1✔
57
            try:
1✔
58
                topojson_data = json.loads(topojson_raw)
1✔
59
            except json.JSONDecodeError:
×
60
                # If parsing fails, fall back to writing the raw string
61
                # as-is but wrapped under a key so the output stays JSON.
62
                topojson_data = {"topojson": topojson_raw}
×
63
        else:
64
            topojson_data = topojson_raw
×
65
        # Determine the target filename based on TEST_ENV setting
66
        if getattr(settings, 'TEST_ENV', False):
1✔
67
            filename = 'country-test.topojson'
1✔
68
        else:
69
            filename = 'country.topojson'
×
70
        # Define the target path (in the source directory)
71
        source_dir = Path(settings.BASE_DIR) / 'source'
1✔
72
        target_path = source_dir / filename
1✔
73
        # Ensure the source directory exists
74
        source_dir.mkdir(exist_ok=True)
1✔
75
        # Write the TopoJSON data to the file
76
        with open(target_path, 'w', encoding='utf-8') as f:
1✔
77
            json.dump(topojson_data, f, ensure_ascii=False, indent=2)
1✔
78
        return str(target_path)
1✔
79
    except json.JSONDecodeError as e:
×
80
        raise ValueError(f"Invalid JSON format in GeoJSON file: {str(e)}")
×
81
    except Exception as e:
×
82
        raise ValueError(f"Failed to process GeoJSON file: {str(e)}")
×
83

84

85
def validate_geojson_file(geojson_file):
1✔
86
    """
87
    Validate that the uploaded file is a valid GeoJSON.
88
    Args:
89
        geojson_file: Django UploadedFile object
90
    Returns:
91
        bool: True if valid, raises ValidationError if not
92
    Raises:
93
        ValueError: If the file is not a valid GeoJSON
94
    """
95
    if not geojson_file:
1✔
96
        raise ValueError("No file provided")
×
97
    if (
1✔
98
        geojson_file.content_type not in [
99
            'application/geo+json', 'application/json', 'text/json'
100
        ]
101
    ):
102
        raise ValueError(
1✔
103
            "Invalid file format. Please upload a valid GeoJSON file."
104
        )
105
    # Try to parse the file as JSON and validate basic GeoJSON structure
106
    try:
1✔
107
        content = geojson_file.read()
1✔
108
        geojson_data = json.loads(content.decode('utf-8'))
1✔
109
        # Reset file pointer
110
        geojson_file.seek(0)
1✔
111
        # Basic GeoJSON validation - must have type property
112
        if not isinstance(geojson_data, dict) or 'type' not in geojson_data:
1✔
113
            raise ValueError("Invalid GeoJSON format: missing 'type' property")
×
114
        # Check if it's a valid GeoJSON type
115
        valid_types = [
1✔
116
            'FeatureCollection', 'Feature', 'Point', 'LineString',
117
            'Polygon', 'MultiPoint', 'MultiLineString', 'MultiPolygon',
118
            'GeometryCollection'
119
        ]
120
        if geojson_data['type'] not in valid_types:
1✔
121
            raise ValueError(f"Invalid GeoJSON type: {geojson_data['type']}")
×
122
        return True
1✔
123
    except json.JSONDecodeError:
×
124
        raise ValueError(
×
125
            "Invalid file format. Please upload a valid GeoJSON file."
126
        )
127
    except UnicodeDecodeError:
×
128
        raise ValueError(
×
129
            "Invalid file encoding. Please upload a UTF-8 encoded file."
130
        )
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc