Merge ~pieq/oem-qa-autosummary:fix-1945724-image-url into oem-qa-autosummary:master

Proposed by Pierre Equoy
Status: Merged
Approved by: Pierre Equoy
Approved revision: 81acbf987a25a4bc243f829afc1be65b7fb77aec
Merged at revision: 1339810a70127fd5baa8db7bd4387da1bba39f06
Proposed branch: ~pieq/oem-qa-autosummary:fix-1945724-image-url
Merge into: oem-qa-autosummary:master
Diff against target: 172 lines (+35/-28)
4 files modified
autosummary/summary.py (+28/-21)
webapp/__init__.py (+3/-3)
webapp/static/style.css (+1/-1)
webapp/templates/image_info.html (+3/-3)
Reviewer Review Type Date Requested Status
Nara Huang (community) Approve
StanleyHuang Approve
Review via email: mp+414387@code.launchpad.net

Description of the change

See issue lp:1945724 for more context.

With this MR, if auto-summary cannot find the mail.content file that contains the image name and the image sha256, it will ask user to manually enter the full URL to that image, and use it to compute the WebDAV URI to download the associated sha256 file that contains the image sha256 value.

This is a bit more flexible than the current situation where user can only input the image name, and not the URL.

To see it in action, check the following screen recording:

https://drive.google.com/file/d/1-ufHaCMXlMpfABC-i4gkpq957l6wVOrd/view?usp=sharing

1. The carlsbad project is selected, with the x03 milestone.
2. The mail.content file cannot be found, so user manually inputs full URL to the image name
3. The webapp computes the WebDAV URI to point to the file containing the sha256 value and use it in the generated summary.

NOTE: In the above video, I use the x03 milestone, but then paste the URL to a x02 milestone image. This is for demonstration purpose only, because the x03 milestone for that project is actually not out yet.

To post a comment you must log in.
Revision history for this message
StanleyHuang (stanley31) wrote :

LGTM +1

review: Approve
Revision history for this message
Nara Huang (narahuang) wrote :

LGTM +1

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
diff --git a/autosummary/summary.py b/autosummary/summary.py
index 9a08276..6117361 100644
--- a/autosummary/summary.py
+++ b/autosummary/summary.py
@@ -12,23 +12,19 @@ WEBDAV_CREDENTIALS = os.environ.get("WEBDAV_CREDENTIALS")
1212
13class MailContentNotFoundError(FileNotFoundError):13class MailContentNotFoundError(FileNotFoundError):
14 """Raised when the mail.content file is not found on the WebDAV server"""14 """Raised when the mail.content file is not found on the WebDAV server"""
15 logging.error("the mail.content file is not found on the WebDAV server")
16 pass
1715
1816
19class Sha256FileNotFoundError(FileNotFoundError):17class Sha256FileNotFoundError(FileNotFoundError):
20 """Raised when the sha256sum file for a given image is not found on the WebDAV server"""18 """Raised when the sha256sum file for a given image is not found on the WebDAV server"""
21 logging.error(
22 "sha256sum file for a given image is not found on the WebDAV server")
23 pass
2419
2520
26class Summary:21class Summary:
27 def __init__(self, launchpad, project_name, milestone_name, image_name=""):22 def __init__(self, launchpad, project_name, milestone_name, image_url=""):
28 logging.info("Initailizing Summary object")23 logging.info("Initailizing Summary object")
29 self.project_name = project_name24 self.project_name = project_name
30 self.milestone_name = milestone_name25 self.milestone_name = milestone_name
31 self.launchpad = launchpad26 self.launchpad = launchpad
27 self.image_url = image_url
3228
33 logging.info((f"Preparing launchpad API for {self.project_name} "29 logging.info((f"Preparing launchpad API for {self.project_name} "
34 f"- {self.milestone_name}"))30 f"- {self.milestone_name}"))
@@ -45,11 +41,14 @@ class Summary:
45 logging.info(f"Preparing WebDAV API as user {user}")41 logging.info(f"Preparing WebDAV API as user {user}")
46 self.webdav_client = Client(webdav_options)42 self.webdav_client = Client(webdav_options)
4743
48 if not image_name:44 if self.image_url:
49 self.image_name, self.image_sha256 = self.get_image_info_from_mail_content()45 # Image name is the last element of the URL, e.g.:
50 else:46 # https://oem-share.canonical.com/partners/project/share/uc20/uc20-alpha/iot-blabla-x01-20201228-9.img.xz
51 self.image_name = image_name47 # -> iot-blabla-x01-20201228-9.img.xz
48 self.image_name = self.image_url.split("/")[-1]
52 self.image_sha256 = self.get_image_sha256()49 self.image_sha256 = self.get_image_sha256()
50 else:
51 self.image_name, self.image_sha256 = self.get_image_info_from_mail_content()
53 self.image_date = get_date_from_image_name(self.image_name)52 self.image_date = get_date_from_image_name(self.image_name)
54 self.new_bugs = self.get_new_bugs()53 self.new_bugs = self.get_new_bugs()
55 self.milestone_bugs = self.lp_milestone.searchTasks()54 self.milestone_bugs = self.lp_milestone.searchTasks()
@@ -136,7 +135,7 @@ class Summary:
136135
137 image_name = image_path.strip().split("/")[-1]136 image_name = image_path.strip().split("/")[-1]
138 except Exception:137 except Exception:
139 raise MailContentNotFoundError()138 raise MailContentNotFoundError("the mail.content file is not found on the WebDAV server")
140139
141 logging.info(f"Image: {image_name}, SHA256: {image_sha256}")140 logging.info(f"Image: {image_name}, SHA256: {image_sha256}")
142 return image_name, image_sha256141 return image_name, image_sha256
@@ -144,7 +143,7 @@ class Summary:
144 def get_image_sha256(self):143 def get_image_sha256(self):
145 """144 """
146 If the image information cannot be automatically parsed from mail.content,145 If the image information cannot be automatically parsed from mail.content,
147 user can manually input the image name, and this function will try to parse146 user can manually input the image URL, and this function will try to parse
148 the corresponding sha256sum file to retrieve the sha256 value.147 the corresponding sha256sum file to retrieve the sha256 value.
149 """148 """
150 buff = io.BytesIO()149 buff = io.BytesIO()
@@ -156,20 +155,28 @@ class Summary:
156 content = buff.read()155 content = buff.read()
157 sha256 = content.decode().split()[0]156 sha256 = content.decode().split()[0]
158 except Exception:157 except Exception:
159 raise Sha256FileNotFoundError()158 raise Sha256FileNotFoundError("sha256sum file for a given image is not found on the WebDAV server")
160159
161 return sha256160 return sha256
162161
163 def get_milestone_uri(self):162 def get_milestone_uri(self):
164 # For projects not using series (i.e. using the default "trunk"163 """Generate WebDAV URI for the selected milestone"""
165 # series), the default is to put the releases in a `releases` directory164 # If the image URL is manually provided by user, we recreate the WebDAV URI from it, e.g.:
166 # on WebDAV.165 # https://oem-share.canonical.com/partners/project/share/uc-20/uc20-x01/iot-blabla-x01-20201228-9.img.xz
167 if self.series_name == "trunk":166 # -> /share/project/uc20/uc20-x01
168 uri = f"/share/{self.project_name}/releases/{self.milestone_name}/"167 if self.image_url:
168 url_list = self.image_url.split("/")
169 uri = f"/share/{self.project_name}/{url_list[-3]}/{url_list[-2]}/"
169 else:170 else:
170 uri = (171 # For projects not using series (i.e. using the default "trunk"
171 f"/share/{self.project_name}/{self.series_name}/{self.milestone_name}/"172 # series), the default is to put the releases in a `releases` directory
172 )173 # on WebDAV.
174 if self.series_name == "trunk":
175 uri = f"/share/{self.project_name}/releases/{self.milestone_name}/"
176 else:
177 uri = (
178 f"/share/{self.project_name}/{self.series_name}/{self.milestone_name}/"
179 )
173 return uri180 return uri
174181
175182
diff --git a/webapp/__init__.py b/webapp/__init__.py
index befaea5..755a829 100644
--- a/webapp/__init__.py
+++ b/webapp/__init__.py
@@ -119,7 +119,7 @@ def generate_summary():
119 logging.info("Route to summary page")119 logging.info("Route to summary page")
120 project = request.form.get("project")120 project = request.form.get("project")
121 milestone = request.form.get("milestone")121 milestone = request.form.get("milestone")
122 image_name = request.form.get("image_name")122 image_url = request.form.get("image_url")
123 hardware = request.form.get("hardware")123 hardware = request.form.get("hardware")
124 stress_test_reports = request.form.get("stress_test_reports")124 stress_test_reports = request.form.get("stress_test_reports")
125 full_test_reports = request.form.get("full_test_reports")125 full_test_reports = request.form.get("full_test_reports")
@@ -130,7 +130,7 @@ def generate_summary():
130 return redirect(url_for("index"))130 return redirect(url_for("index"))
131131
132 try:132 try:
133 report = summary.Summary(launchpad, project, milestone, image_name=image_name)133 report = summary.Summary(launchpad, project, milestone, image_url=image_url)
134 critical_new_bugs = [b for b in report.new_bugs if b.importance == "Critical"]134 critical_new_bugs = [b for b in report.new_bugs if b.importance == "Critical"]
135 high_new_bugs = [b for b in report.new_bugs if b.importance == "High"]135 high_new_bugs = [b for b in report.new_bugs if b.importance == "High"]
136 critical_reopened_bugs = [136 critical_reopened_bugs = [
@@ -156,7 +156,7 @@ def generate_summary():
156 )156 )
157 except summary.Sha256FileNotFoundError:157 except summary.Sha256FileNotFoundError:
158 error = (158 error = (
159 f"Could not find sha256 for image named {image_name}. "159 f"Could not find sha256 for image at {image_url}. "
160 f"Please make sure this image is available on oem-share."160 f"Please make sure this image is available on oem-share."
161 )161 )
162 logging.error(error)162 logging.error(error)
diff --git a/webapp/static/style.css b/webapp/static/style.css
index 3ef3f96..b45b806 100644
--- a/webapp/static/style.css
+++ b/webapp/static/style.css
@@ -11,7 +11,7 @@ main {
11input, textarea, select {11input, textarea, select {
12 line-height: 1.5em;12 line-height: 1.5em;
13 font-size: 1em;13 font-size: 1em;
14 width: 40em;14 width: 50em;
15 padding: 0.5em;15 padding: 0.5em;
16}16}
1717
diff --git a/webapp/templates/image_info.html b/webapp/templates/image_info.html
index 6828be6..eb76be1 100644
--- a/webapp/templates/image_info.html
+++ b/webapp/templates/image_info.html
@@ -17,15 +17,15 @@
17 <p class="warning"><strong>Error:</strong> {{ error }}</p>17 <p class="warning"><strong>Error:</strong> {{ error }}</p>
18 {% endif %}18 {% endif %}
19 <p>Auto-Summary relies on the release e-mail (stored on oem-share as a <code>mail.content</code> file) to retrieve the image name and its SHA256 value.</p>19 <p>Auto-Summary relies on the release e-mail (stored on oem-share as a <code>mail.content</code> file) to retrieve the image name and its SHA256 value.</p>
20 <p>If you want, you can manually specify the image name, and Auto-Summary will try to figure out the rest.</p>20 <p>If you want, you can manually specify the image URL, and Auto-Summary will try to figure out the rest.</p>
21 <p>Note: In the normal process, a release e-mail should always be sent, and the <code>mail.content</code> file should always be available in the project's oem-share. If it is not the case, please contact the Software Engineering team to review the process.</p>21 <p>Note: In the normal process, a release e-mail should always be sent, and the <code>mail.content</code> file should always be available in the project's oem-share. If it is not the case, please contact the Software Engineering team to review the process.</p>
2222
23 <hr>23 <hr>
24<form action="{{ url_for('generate_summary') }}" method="post">24<form action="{{ url_for('generate_summary') }}" method="post">
25 <ul>25 <ul>
26 <li>26 <li>
27 <label for="html_report">Image name:</label>27 <label for="html_report">Image URL:</label>
28 <input type="text" id="image_name" name="image_name" placeholder="e.g. iot-fisterra-nitrogen6x-classic-server-2004-x01-20201228-9.img.xz">28 <input type="text" id="image_url" name="image_url" placeholder="e.g. https://oem-share.canonical.com/partners/project/share/uc20/uc20-x01/iot-project-uc20-x01-20201228-9.img.xz">
29 </li>29 </li>
30 <li class="button">30 <li class="button">
31 <button type="submit">Generate Summary</button>31 <button type="submit">Generate Summary</button>

Subscribers

People subscribed via source and target branches

to all changes: