Coverage for webapp/publisher/snaps/listing_views.py: 72%
151 statements
« prev ^ index » next coverage.py v7.8.0, created at 2025-04-28 22:05 +0000
« prev ^ index » next coverage.py v7.8.0, created at 2025-04-28 22:05 +0000
1# Standard library
2from json import loads
4# Packages
5import bleach
6import flask
7from canonicalwebteam.exceptions import (
8 StoreApiError,
9 StoreApiResponseErrorList,
10)
11from canonicalwebteam.store_api.dashboard import Dashboard
12from canonicalwebteam.store_api.devicegw import DeviceGW
14# Local
15from webapp import helpers
16from webapp.helpers import api_session
17from webapp.decorators import login_required
18from webapp.markdown import parse_markdown_description
19from webapp.publisher.snaps import logic, preview_data
20from webapp.store.logic import (
21 filter_screenshots,
22 get_categories,
23 get_video,
24)
26dashboard = Dashboard(api_session)
27device_gateway = DeviceGW("snap", api_session)
30def get_market_snap(snap_name):
31 return flask.redirect(
32 flask.url_for(".get_listing_data", snap_name=snap_name)
33 )
36def redirect_post_market_snap(snap_name):
37 return flask.redirect(
38 flask.url_for(".post_listing_data", snap_name=snap_name)
39 )
42@login_required
43def get_listing_data(snap_name):
44 snap_details = dashboard.get_snap_info(flask.session, snap_name)
46 details_metrics_enabled = snap_details["public_metrics_enabled"]
47 details_blacklist = snap_details.get("public_metrics_blacklist", [])
49 # Filter icon & screenshot urls from the media set.
50 icon_urls, screenshot_urls, banner_urls = logic.categorise_media(
51 snap_details["media"]
52 )
54 primary_website = ""
55 if (
56 "website" in snap_details["links"]
57 and len(snap_details["links"]["website"]) > 0
58 ):
59 primary_website = snap_details["links"]["website"][0]
61 websites = []
62 if "website" in snap_details["links"]:
63 if len(snap_details["links"]["website"]) > 1:
64 snap_details["links"]["website"].pop(0)
65 for website in snap_details["links"]["website"]:
66 websites.append({"url": website})
68 def format_links(key):
69 result = []
70 if key in snap_details["links"]:
71 for url in snap_details["links"][key]:
72 result.append({"url": url})
73 return result
75 licenses = []
76 for license in helpers.get_licenses():
77 licenses.append({"key": license["licenseId"], "name": license["name"]})
79 license = snap_details["license"]
80 license_type = "custom"
82 if " AND " not in license.upper() and " WITH " not in license.upper():
83 license_type = "simple"
85 try:
86 categories_results = device_gateway.get_categories()
87 except StoreApiError:
88 categories_results = []
90 categories = sorted(
91 get_categories(categories_results),
92 key=lambda category: category["slug"],
93 )
95 snap_categories = logic.replace_reserved_categories_key(
96 snap_details["categories"]
97 )
99 snap_categories = logic.filter_categories(snap_categories)
101 snap_categories["categories"] = [
102 category["name"] for category in snap_categories["categories"]
103 ]
105 filename = "publisher/content/listing_tour.yaml"
106 tour_steps = helpers.get_yaml(filename, typ="rt")
108 primary_category = ""
109 if len(snap_categories["categories"]) > 0:
110 primary_category = snap_categories["categories"][0]
112 secondary_category = ""
113 if len(snap_categories["categories"]) > 1:
114 secondary_category = snap_categories["categories"][1]
116 video_urls = None
118 if len(snap_details["video_urls"]) > 0:
119 video_urls = snap_details["video_urls"][0]
121 context = {
122 "title": snap_details["title"],
123 "video_urls": video_urls,
124 "summary": snap_details["summary"],
125 "description": snap_details["description"],
126 "categories": categories,
127 "primary_category": primary_category,
128 "secondary_category": secondary_category,
129 "websites": websites,
130 "contacts": format_links("contact"),
131 "donations": format_links("donations"),
132 "source_code": format_links("source"),
133 "issues": format_links("issues"),
134 "primary_website": primary_website,
135 "snap_id": snap_details["snap_id"],
136 "public_metrics_enabled": details_metrics_enabled,
137 "public_metrics_blacklist": details_blacklist,
138 "license": license,
139 "license_type": license_type,
140 "licenses": licenses,
141 "icon_url": icon_urls[0] if icon_urls else None,
142 "screenshot_urls": screenshot_urls,
143 "banner_urls": banner_urls,
144 "update_metadata_on_release": snap_details[
145 "update_metadata_on_release"
146 ],
147 "tour_steps": tour_steps,
148 }
150 res = {}
151 res["data"] = context
152 res["message"] = ""
153 res["success"] = True
155 return flask.jsonify(res)
158@login_required
159def get_listing_snap(snap_name):
160 snap_details = dashboard.get_snap_info(flask.session, snap_name)
161 token = ""
162 if snap_details["links"]["website"]:
163 token = helpers.get_dns_verification_token(
164 snap_details["snap_name"], snap_details["links"]["website"][0]
165 )
166 return flask.render_template(
167 "store/publisher.html",
168 snap_name=snap_name,
169 dns_verification_token=token,
170 )
173@login_required
174def post_listing_data(snap_name):
175 changes = None
176 changed_fields = flask.request.form.get("changes")
178 if changed_fields:
179 changes = loads(changed_fields)
181 if changes:
182 snap_id = flask.request.form.get("snap_id")
183 error_list = []
185 if "images" in changes:
186 # Add existing screenshots
187 current_screenshots = dashboard.snap_screenshots(
188 flask.session, snap_id
189 )
191 icon_input = (
192 flask.request.files.get("icon")
193 if flask.request.files.get("icon")
194 else None
195 )
196 screenshots_input = [
197 s if s else None
198 for s in flask.request.files.getlist("screenshots")
199 ]
200 banner_image_input = (
201 flask.request.files.get("banner-image")
202 if flask.request.files.get("banner-image")
203 else None
204 )
206 images_json, images_files = logic.build_changed_images(
207 changes["images"],
208 current_screenshots,
209 icon_input,
210 screenshots_input,
211 banner_image_input,
212 )
214 try:
215 dashboard.snap_screenshots(
216 flask.session, snap_id, images_json, images_files
217 )
218 except StoreApiResponseErrorList as api_response_error_list:
219 if api_response_error_list.status_code != 404:
220 error_list = error_list + api_response_error_list.errors
222 body_json = logic.filter_changes_data(changes)
224 if body_json:
225 if "description" in body_json:
226 body_json["description"] = logic.remove_invalid_characters(
227 body_json["description"]
228 )
230 try:
231 dashboard.snap_metadata(flask.session, snap_id, body_json)
232 except StoreApiResponseErrorList as api_response_error_list:
233 if api_response_error_list.status_code != 404:
234 error_list = error_list + api_response_error_list.errors
236 if error_list:
237 try:
238 snap_details = dashboard.get_snap_info(
239 flask.session, snap_name
240 )
241 except StoreApiResponseErrorList as api_response_error_list:
242 if api_response_error_list.status_code == 404:
243 return flask.abort(
244 404, "No snap named {}".format(snap_name)
245 )
246 else:
247 error_list = error_list + api_response_error_list.errors
249 licenses = []
250 for license in helpers.get_licenses():
251 licenses.append(
252 {"key": license["licenseId"], "name": license["name"]}
253 )
255 license = snap_details["license"]
257 snap_categories = logic.replace_reserved_categories_key(
258 snap_details["categories"]
259 )
261 snap_categories = logic.filter_categories(snap_categories)
263 res = {"success", True}
265 return flask.make_response(res, 200)
267 return flask.make_response({}, 200)
270@login_required
271def post_preview(snap_name):
272 snap_details = dashboard.get_snap_info(flask.session, snap_name)
274 context = {
275 "publisher": snap_details["publisher"]["display-name"],
276 "username": snap_details["publisher"]["username"],
277 "developer_validation": snap_details["publisher"]["validation"],
278 "categories": [],
279 }
281 state = loads(flask.request.form["state"])
283 for item in state:
284 if item == "description":
285 context[item] = parse_markdown_description(
286 bleach.clean(state[item], tags=[])
287 )
288 else:
289 context[item] = state[item]
291 context["is_preview"] = True
292 context["package_name"] = context["snap_name"]
293 context["snap_title"] = context["title"]
294 context["appliances"] = []
296 # Images
297 icon = helpers.get_icon(context["images"])
298 context["screenshots"] = filter_screenshots(context["images"])
299 context["icon_url"] = icon
301 if context["video"]:
302 context["video"] = get_video(context["video"])
304 # Channel map
305 context["channel_map"] = []
306 context["default_track"] = "latest"
307 context["lowest_risk_available"] = "stable"
308 context["version"] = "test"
309 context["has_stable"] = True
311 # metadata
312 context["last_updated"] = "Preview"
313 context["filesize"] = "1mb"
315 # maps
316 context["countries"] = preview_data.get_countries()
317 context["normalized_os"] = preview_data.get_normalised_oses()
319 return flask.render_template("store/snap-details.html", **context)