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

Problematy / goodmap / 15286395652

27 May 2025 09:46PM UTC coverage: 97.241% (-0.8%) from 98.034%
15286395652

Pull #226

github

web-flow
Merge 4092ec208 into 4731d8e6f
Pull Request #226: Platzky redirections error

118 of 123 branches covered (95.93%)

Branch coverage included in aggregate %.

17 of 18 new or added lines in 1 file covered. (94.44%)

305 of 312 relevant lines covered (97.76%)

0.98 hits per line

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

97.08
/goodmap/core_api.py
1
import importlib.metadata
1✔
2
import uuid
1✔
3

4
from flask import Blueprint, jsonify, make_response, request
1✔
5
from flask_babel import gettext
1✔
6
from flask_restx import Api, Resource, fields
1✔
7
from platzky.config import LanguagesMapping
1✔
8

9
from goodmap.formatter import prepare_pin
1✔
10

11

12
def make_tuple_translation(keys_to_translate):
1✔
13
    return [(x, gettext(x)) for x in keys_to_translate]
1✔
14

15

16
def get_or_none(data, *keys):
1✔
17
    for key in keys:
1✔
18
        if isinstance(data, dict):
1✔
19
            data = data.get(key)
1✔
20
        else:
NEW
21
            return None
×
22
    return data
1✔
23

24

25
def core_pages(
1✔
26
    database, languages: LanguagesMapping, notifier_function, csrf_generator, location_model
27
) -> Blueprint:
28
    core_api_blueprint = Blueprint("api", __name__, url_prefix="/api")
1✔
29
    core_api = Api(core_api_blueprint, doc="/doc", version="0.1")
1✔
30

31
    location_report_model = core_api.model(
1✔
32
        "LocationReport",
33
        {
34
            "id": fields.String(required=True, description="Location ID"),
35
            "description": fields.String(required=True, description="Description of the problem"),
36
        },
37
    )
38

39
    # TODO get this from Location pydantic model
40
    suggested_location_model = core_api.model(
1✔
41
        "LocationSuggestion",
42
        {
43
            "name": fields.String(required=False, description="Organization name"),
44
            "position": fields.String(required=True, description="Location of the suggestion"),
45
            "photo": fields.String(required=False, description="Photo of the location"),
46
        },
47
    )
48

49
    @core_api.route("/suggest-new-point")
1✔
50
    class NewLocation(Resource):
1✔
51
        @core_api.expect(suggested_location_model)
1✔
52
        def post(self):
1✔
53
            """Suggest new location"""
54
            try:
1✔
55
                suggested_location = request.get_json()
1✔
56
                suggested_location.update({"uuid": str(uuid.uuid4())})
1✔
57
                location = location_model.model_validate(suggested_location)
1✔
58
                message = (
1✔
59
                    f"A new location has been suggested under uuid: '{location.uuid}' "
60
                    f"at position: {location.position}"
61
                )
62
                notifier_function(message)
1✔
63
            except ValueError as e:
1✔
64
                return make_response(jsonify({"message": f"Invalid location data: {e}"}), 400)
1✔
65
            except Exception as e:
1✔
66
                return make_response(jsonify({"message": f"Error sending notification : {e}"}), 400)
1✔
67
            return make_response(jsonify({"message": "Location suggested"}), 200)
1✔
68

69
    @core_api.route("/report-location")
1✔
70
    class ReportLocation(Resource):
1✔
71
        @core_api.expect(location_report_model)
1✔
72
        def post(self):
1✔
73
            """Report location"""
74
            try:
1✔
75
                location_report = request.get_json()
1✔
76
                message = (
1✔
77
                    f"A location has been reported: '{location_report['id']}' "
78
                    f"with problem: {location_report['description']}"
79
                )
80
                notifier_function(message)
1✔
81
            except Exception as e:
1✔
82
                error_message = gettext("Error sending notification")
1✔
83
                return make_response(jsonify({"message": f"{error_message} : {e}"}), 400)
1✔
84
            return make_response(jsonify({"message": gettext("Location reported")}), 200)
1✔
85

86
    @core_api.route("/locations")
1✔
87
    class GetLocations(Resource):
1✔
88
        def get(self):
1✔
89
            """
90
            Shows list of locations with uuid and position
91
            """
92
            query_params = request.args.to_dict(flat=False)
1✔
93
            all_locations = database.get_locations(query_params)
1✔
94
            return jsonify([x.basic_info() for x in all_locations])
1✔
95

96
    @core_api.route("/location/<location_id>")
1✔
97
    class GetLocation(Resource):
1✔
98
        def get(self, location_id):
1✔
99
            """
100
            Shows a single location with all data
101
            """
102
            location = database.get_location(location_id)
1✔
103

104
            # TODO getting visible_data and meta_data should be taken from db methods
105
            #    e.g. db.get_visible_data() and db.get_meta_data()
106
            #    visible_data and meta_data should be models
107
            all_data = database.get_data()
1✔
108
            visible_data = all_data["visible_data"]
1✔
109
            meta_data = all_data["meta_data"]
1✔
110

111
            formatted_data = prepare_pin(location.model_dump(), visible_data, meta_data)
1✔
112
            return jsonify(formatted_data)
1✔
113

114
    @core_api.route("/version")
1✔
115
    class Version(Resource):
1✔
116
        def get(self):
1✔
117
            """Shows backend version"""
118
            version_info = {"backend": importlib.metadata.version("goodmap")}
1✔
119
            return jsonify(version_info)
1✔
120

121
    @core_api.route("/categories")
1✔
122
    class Categories(Resource):
1✔
123
        def get(self):
1✔
124
            """Shows all available categories"""
125
            all_data = database.get_data()
1✔
126
            categories = make_tuple_translation(all_data["categories"].keys())
1✔
127

128
            categories_help = all_data["categories_help"]
1✔
129
            proper_categories_help = []
1✔
130
            if categories_help is not None:
1✔
131
                for option in categories_help:
1✔
132
                    proper_categories_help.append({option: gettext(f"categories_help_{option}")})
1✔
133

134
            return jsonify({"categories": categories, "categories_help": proper_categories_help})
1!
135

136
    @core_api.route("/languages")
1✔
137
    class Languages(Resource):
1✔
138
        def get(self):
1✔
139
            """Shows all available languages"""
140
            return jsonify(languages)
1✔
141

142
    @core_api.route("/category/<category_type>")
1✔
143
    class CategoryTypes(Resource):
1✔
144
        def get(self, category_type):
1✔
145
            """Shows all available types in category"""
146
            all_data = database.get_data()
1✔
147
            local_data = make_tuple_translation(all_data["categories"][category_type])
1✔
148

149
            categories_options_help = get_or_none(
1✔
150
                all_data, "categories_options_help", category_type
151
            )
152
            proper_categories_options_help = []
1✔
153
            if categories_options_help is not None:
1✔
154
                for option in categories_options_help:
1✔
155
                    proper_categories_options_help.append(
1✔
156
                        {option: gettext(f"categories_options_help_{option}")}
157
                    )
158

159
            return jsonify(
1!
160
                {
161
                    "categories_options": local_data,
162
                    "categories_options_help": proper_categories_options_help,
163
                }
164
            )
165

166
    @core_api.route("/generate-csrf-token")
1✔
167
    class CsrfToken(Resource):
1✔
168
        def get(self):
1✔
169
            csrf_token = csrf_generator()
1✔
170
            return {"csrf_token": csrf_token}
1✔
171

172
    return core_api_blueprint
1✔
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