Skip to content

Commit 6dec579

Browse files
authored
ci: merge main to release (ietf-tools#8138)
2 parents d07ab12 + 91f15b1 commit 6dec579

6 files changed

Lines changed: 98 additions & 53 deletions

File tree

.github/workflows/build.yml

Lines changed: 7 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -363,33 +363,15 @@ jobs:
363363
364364
- name: Destroy Build VM + resources
365365
if: always()
366-
shell: pwsh
367366
run: |
368-
echo "Destroying VM..."
367+
echo "Terminate VM..."
369368
az vm delete -g ghaDatatracker -n tmpGhaBuildVM-${{ github.run_number }} --yes --force-deletion true
370-
371-
# $resourceOrderRemovalOrder = [ordered]@{
372-
# "Microsoft.Compute/virtualMachines" = 0
373-
# "Microsoft.Compute/disks" = 1
374-
# "Microsoft.Network/networkInterfaces" = 2
375-
# "Microsoft.Network/publicIpAddresses" = 3
376-
# "Microsoft.Network/networkSecurityGroups" = 4
377-
# "Microsoft.Network/virtualNetworks" = 5
378-
# }
379-
# echo "Fetching remaining resources..."
380-
# $resources = az resource list --resource-group ghaDatatracker | ConvertFrom-Json
381-
382-
# $orderedResources = $resources
383-
# | Sort-Object @{
384-
# Expression = {$resourceOrderRemovalOrder[$_.type]}
385-
# Descending = $False
386-
# }
387-
388-
# echo "Deleting remaining resources..."
389-
# $orderedResources | ForEach-Object {
390-
# az resource delete --resource-group ghaDatatracker --ids $_.id --verbose
391-
# }
392-
369+
echo "Delete Public IP..."
370+
az resource delete -g ghaDatatracker -n tmpGhaBuildVM-${{ github.run_number }}PublicIP --resource-type "Microsoft.Network/publicIPAddresses"
371+
echo "Delete Network Security Group..."
372+
az resource delete -g ghaDatatracker -n tmpGhaBuildVM-${{ github.run_number }}NSG --resource-type "Microsoft.Network/networkSecurityGroups"
373+
echo "Delete Virtual Network..."
374+
az resource delete -g ghaDatatracker -n tmpGhaBuildVM-${{ github.run_number }}VNET --resource-type "Microsoft.Network/virtualNetworks"
393375
echo "Logout from Azure..."
394376
az logout
395377

.github/workflows/tests.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,12 @@ on:
1111
skipSelenium:
1212
description: 'Skip Selenium Tests'
1313
default: false
14-
required: true
14+
required: false
1515
type: boolean
1616
targetBaseVersion:
1717
description: 'Target Base Image Version'
1818
default: latest
19-
required: true
19+
required: false
2020
type: string
2121

2222
jobs:

dev/tests/debug.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
# This script recreate the same environment used during tests on GitHub Actions
44
# and drops you into a terminal at the point where the actual tests would be run.
55
#
6-
# Refer to https://github.com/ietf-tools/datatracker/blob/main/.github/workflows/build.yml#L141-L155
6+
# Refer to https://github.com/ietf-tools/datatracker/blob/main/.github/workflows/tests.yml#L47-L66
77
# for the commands to run next.
88
#
99
# Simply type "exit" + ENTER to exit and shutdown this test environment.

ietf/meeting/tests_views.py

Lines changed: 55 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -125,8 +125,12 @@ def tearDown(self):
125125
settings.MEETINGHOST_LOGO_PATH = self.saved_meetinghost_logo_path
126126
super().tearDown()
127127

128-
def write_materials_file(self, meeting, doc, content, charset="utf-8"):
129-
path = os.path.join(self.materials_dir, "%s/%s/%s" % (meeting.number, doc.type_id, doc.uploaded_filename))
128+
def write_materials_file(self, meeting, doc, content, charset="utf-8", with_ext=None):
129+
if with_ext is None:
130+
filename = doc.uploaded_filename
131+
else:
132+
filename = Path(doc.uploaded_filename).with_suffix(with_ext)
133+
path = os.path.join(self.materials_dir, "%s/%s/%s" % (meeting.number, doc.type_id, filename))
130134

131135
dirname = os.path.dirname(path)
132136
if not os.path.exists(dirname):
@@ -753,7 +757,56 @@ def test_materials_has_edit_links(self):
753757
)
754758
self.assertEqual(len(q(f'a[href^="{edit_url}#session"]')), 1, f'Link to session_details page for {acro}')
755759

760+
def test_materials_document_extension_choice(self):
761+
def _url(**kwargs):
762+
return urlreverse("ietf.meeting.views.materials_document", kwargs=kwargs)
763+
764+
presentation = SessionPresentationFactory(
765+
document__rev="00",
766+
document__name="slides-whatever",
767+
document__uploaded_filename="slides-whatever-00.txt",
768+
document__type_id="slides",
769+
document__states=(("reuse_policy", "single"),)
770+
)
771+
session = presentation.session
772+
meeting = session.meeting
773+
# This is not a realistic set of files to exist, but is useful for testing. Normally,
774+
# we'd have _either_ txt, pdf, or pptx + pdf.
775+
self.write_materials_file(meeting, presentation.document, "Hi I'm a txt", with_ext=".txt")
776+
self.write_materials_file(meeting, presentation.document, "Hi I'm a pptx", with_ext=".pptx")
777+
778+
# with no rev, prefers the uploaded_filename
779+
r = self.client.get(_url(document="slides-whatever", num=meeting.number)) # no rev
780+
self.assertEqual(r.status_code, 200)
781+
self.assertEqual(r.content.decode(), "Hi I'm a txt")
782+
783+
# with a rev, prefers pptx because it comes first alphabetically
784+
r = self.client.get(_url(document="slides-whatever-00", num=meeting.number))
785+
self.assertEqual(r.status_code, 200)
786+
self.assertEqual(r.content.decode(), "Hi I'm a pptx")
756787

788+
# now create a pdf
789+
self.write_materials_file(meeting, presentation.document, "Hi I'm a pdf", with_ext=".pdf")
790+
791+
# with no rev, still prefers uploaded_filename
792+
r = self.client.get(_url(document="slides-whatever", num=meeting.number)) # no rev
793+
self.assertEqual(r.status_code, 200)
794+
self.assertEqual(r.content.decode(), "Hi I'm a txt")
795+
796+
# pdf should be preferred with a rev
797+
r = self.client.get(_url(document="slides-whatever-00", num=meeting.number))
798+
self.assertEqual(r.status_code, 200)
799+
self.assertEqual(r.content.decode(), "Hi I'm a pdf")
800+
801+
# and explicit extensions should, of course, be respected
802+
for ext in ["pdf", "pptx", "txt"]:
803+
r = self.client.get(_url(document="slides-whatever-00", num=meeting.number, ext=f".{ext}"))
804+
self.assertEqual(r.status_code, 200)
805+
self.assertEqual(r.content.decode(), f"Hi I'm a {ext}")
806+
807+
# and 404 should come up if the ext is not found
808+
r = self.client.get(_url(document="slides-whatever-00", num=meeting.number, ext=".docx"))
809+
self.assertEqual(r.status_code, 404)
757810

758811
def test_materials_editable_groups(self):
759812
meeting = make_meeting_test_data()

ietf/meeting/urls.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ def get_redirect_url(self, *args, **kwargs):
8383
url(r'^week-view(?:.html)?/?$', AgendaRedirectView.as_view(pattern_name='agenda', permanent=True)),
8484
url(r'^materials(?:.html)?/?$', views.materials),
8585
url(r'^request_minutes/?$', views.request_minutes),
86-
url(r'^materials/%(document)s((?P<ext>\.[a-z0-9]+)|/)?$' % settings.URL_REGEXPS, views.materials_document),
86+
url(r'^materials/%(document)s(?P<ext>\.[a-z0-9]+)?/?$' % settings.URL_REGEXPS, views.materials_document),
8787
url(r'^session/?$', views.materials_editable_groups),
8888
url(r'^proceedings(?:.html)?/?$', views.proceedings),
8989
url(r'^proceedings(?:.html)?/finalize/?$', views.finalize_proceedings),

ietf/meeting/views.py

Lines changed: 32 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44

55
import csv
66
import datetime
7-
import glob
87
import io
98
import itertools
109
import json
@@ -20,6 +19,7 @@
2019
from collections import OrderedDict, Counter, deque, defaultdict, namedtuple
2120
from functools import partialmethod
2221
import jsonschema
22+
from pathlib import Path
2323
from urllib.parse import parse_qs, unquote, urlencode, urlsplit, urlunsplit
2424
from tempfile import mkstemp
2525
from wsgiref.handlers import format_date_time
@@ -250,40 +250,49 @@ def _get_materials_doc(meeting, name):
250250

251251
@cache_page(1 * 60)
252252
def materials_document(request, document, num=None, ext=None):
253-
meeting=get_meeting(num,type_in=['ietf','interim'])
253+
"""Materials document view
254+
255+
:param request: Django request
256+
:param document: Name of document without an extension
257+
:param num: meeting number
258+
:param ext: extension including preceding '.'
259+
"""
260+
meeting = get_meeting(num, type_in=["ietf", "interim"])
254261
num = meeting.number
255262
try:
256263
doc, rev = _get_materials_doc(meeting=meeting, name=document)
257264
except Document.DoesNotExist:
258265
raise Http404("No such document for meeting %s" % num)
259266

260267
if not rev:
261-
filename = doc.get_file_name()
268+
filename = Path(doc.get_file_name())
262269
else:
263-
filename = os.path.join(doc.get_file_path(), document)
270+
filename = Path(doc.get_file_path()) / document
264271
if ext:
265-
if not filename.endswith(ext):
266-
name, _ = os.path.splitext(filename)
267-
filename = name + ext
268-
else:
269-
filenames = glob.glob(filename+'.*')
270-
if filenames:
271-
filename = filenames[0]
272-
_, basename = os.path.split(filename)
273-
if not os.path.exists(filename):
274-
raise Http404("File not found: %s" % filename)
272+
filename = filename.with_suffix(ext)
273+
elif filename.suffix == "":
274+
# If we don't already have an extension, try to add one
275+
ext_choices = {
276+
# Construct a map from suffix to full filename
277+
fn.suffix: fn
278+
for fn in sorted(filename.parent.glob(filename.stem + ".*"))
279+
}
280+
if len(ext_choices) > 0:
281+
if ".pdf" in ext_choices:
282+
filename = ext_choices[".pdf"]
283+
else:
284+
filename = list(ext_choices.values())[0]
285+
if not filename.exists():
286+
raise Http404(f"File not found: {filename}")
275287

276288
old_proceedings_format = meeting.number.isdigit() and int(meeting.number) <= 96
277289
if settings.MEETING_MATERIALS_SERVE_LOCALLY or old_proceedings_format:
278-
with io.open(filename, 'rb') as file:
279-
bytes = file.read()
280-
290+
bytes = filename.read_bytes()
281291
mtype, chset = get_mime_type(bytes)
282292
content_type = "%s; charset=%s" % (mtype, chset)
283293

284-
file_ext = os.path.splitext(filename)
285-
if len(file_ext) == 2 and file_ext[1] == '.md' and mtype == 'text/plain':
286-
sorted_accept = sort_accept_tuple(request.META.get('HTTP_ACCEPT'))
294+
if filename.suffix == ".md" and mtype == "text/plain":
295+
sorted_accept = sort_accept_tuple(request.META.get("HTTP_ACCEPT"))
287296
for atype in sorted_accept:
288297
if atype[0] == "text/markdown":
289298
content_type = content_type.replace("plain", "markdown", 1)
@@ -293,7 +302,7 @@ def materials_document(request, document, num=None, ext=None):
293302
"minimal.html",
294303
{
295304
"content": markdown.markdown(bytes.decode(encoding=chset)),
296-
"title": basename,
305+
"title": filename.name,
297306
},
298307
)
299308
content_type = content_type.replace("plain", "html", 1)
@@ -302,11 +311,12 @@ def materials_document(request, document, num=None, ext=None):
302311
break
303312

304313
response = HttpResponse(bytes, content_type=content_type)
305-
response['Content-Disposition'] = 'inline; filename="%s"' % basename
314+
response["Content-Disposition"] = f'inline; filename="{filename.name}"'
306315
return response
307316
else:
308317
return HttpResponseRedirect(redirect_to=doc.get_href(meeting=meeting))
309318

319+
310320
@login_required
311321
def materials_editable_groups(request, num=None):
312322
meeting = get_meeting(num)

0 commit comments

Comments
 (0)