Skip to content

Commit da14695

Browse files
author
Piotr Mitros
committed
Better defaults for RSS, clean JSON in Studio dumps
1 parent 5dd3eaf commit da14695

File tree

4 files changed

+189
-44
lines changed

4 files changed

+189
-44
lines changed

clean_studio_xml.py

+5-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import argparse
22

3-
43
import helpers
54

65
parser = argparse.ArgumentParser(description = "Clean up XML spat out by Studio.")
@@ -41,3 +40,8 @@
4140
# os.mkdir(os.path.join(args.base, 'static'))
4241

4342
helpers.save_url_name_map(args.base)
43+
44+
# Now, we clean up a few JSON files.
45+
for filename in ['policies/edx/policy.json', 'policies/edx/grading_policy.json']:
46+
helpers.clean_json(args.base, filename)
47+

helpers.py

+14-3
Original file line numberDiff line numberDiff line change
@@ -181,7 +181,7 @@ def load_subtree(directory_base, element):
181181
filename = os.path.join(directory_base, element.tag,element.attrib['url_name']+'.xml')
182182

183183
if os.path.isdir(os.path.join(directory_base, element.tag)) and element.attrib.has_key('url_name'):
184-
if not os.path.exists(filename):
184+
if not os.path.exists(filename.encode('utf-8')):
185185
for child in element:
186186
child.parent = element
187187
load_subtree(directory_base,child)
@@ -224,8 +224,10 @@ def save_tree(basepath, tree):
224224
del e.attrib[key]
225225

226226
output = ET.tostring(tree.getroot()) # TODO: Tounicode
227-
output_file = open(os.path.join(basepath, 'course.xml'), "w")
228-
output_file.write(xml.dom.minidom.parseString(output).toprettyxml(indent=' '))
227+
output_file = open(os.path.join(basepath, 'course.xml'), "wb")
228+
md = xml.dom.minidom.parseString(output)
229+
pxml = md.toprettyxml(indent=' ')
230+
output_file.write(pxml.encode('utf-8'))
229231
output_file.close()
230232

231233
yt_service = None
@@ -294,3 +296,12 @@ def format_file_size(num):
294296
return "%3.1f%s" % (num, x)
295297
num /= 1024.0
296298
return "%3.1f%s" % (num, 'TB')
299+
300+
def clean_json(base, filename):
301+
''' If filename exists, load it, and save it, with JSON pretty-printed '''
302+
fn = os.path.join(base, filename)
303+
if os.path.exists(fn):
304+
j = json.load(open(fn))
305+
fp = open(fn, "w")
306+
json.dump(j, fp, indent=2, sort_keys=True)
307+
fp.close()

make_course_rss.py

+13-10
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,9 @@
4040
parser = argparse.ArgumentParser(description = "Generate an RSS feed of a course.")
4141
parser.add_argument("export_base", help="Base directory of Studio-dumped XML")
4242
parser.add_argument("url_base", help="URL the feed will be hosted from")
43-
parser.add_argument("--format", help="Format of RSS feed (mp4, webm, 3gp, or m4a)", default='webm', dest='format')
43+
parser.add_argument("--format", help="Format of RSS feed (mp4, webm, 3gp, or m4a)", default='3gp', dest='format')
4444
parser.add_argument("--course_url", help="URL of the course about page", default="https://www.edx.org/", dest="course_url")
45+
parser.add_argument("--output_dir", help="Output directory", default="output", dest="output_dir")
4546

4647
args = parser.parse_args()
4748

@@ -78,6 +79,7 @@
7879
'url_base' : args.url_base,
7980
'export_base' : args.export_base,
8081
'course_url':args.course_url,
82+
'output_dir':args.output_dir,
8183
'mimetype' : video_format_parameters[video_format]['mimetype'],
8284
'codec_description' : video_format_parameters[video_format]['codec_description'],
8385
'video_codec_name' : video_format_parameters[video_format]['video_codec_name'],
@@ -107,7 +109,7 @@
107109
if 'youtube_id_1_0' not in e.attrib:
108110
continue
109111
youtube_id = e.attrib['youtube_id_1_0']
110-
youtube_cache = os.path.join('output', youtube_id + ".json")
112+
youtube_cache = os.path.join(conf['output_dir'], youtube_id + ".json")
111113
if not os.path.exists(youtube_cache):
112114
youtube_info = helpers.youtube_entry(youtube_id)
113115
f = open(youtube_cache, "w")
@@ -132,7 +134,7 @@
132134

133135

134136
base_filename = youtube_id+"."+conf['video_extension']
135-
dl_filename = os.path.join('output', base_filename)
137+
dl_filename = os.path.join(conf['output_dir'], base_filename)
136138
if not os.path.exists(dl_filename):
137139
command = "youtube-dl -f {fmt} https://www.youtube.com/watch?v={uid} -o {file}".format(fmt=conf['youtube_dl_code'],
138140
uid=youtube_id,
@@ -147,10 +149,10 @@
147149
if len(youtube_description) > 0 and youtube_description[-1]!=' ':
148150
youtube_description = youtube_description + ' '
149151

150-
item_dict['description'] = conf['video_description'].format(youtube_description = youtube_description,
151-
video_location = (" / ".join(description)),
152-
pretty_length = pretty_length,
153-
duration = youtube_info['duration_str'],
152+
item_dict['description'] = conf['video_description'].format(youtube_description = youtube_description.encode('utf-8'),
153+
video_location = (" / ".join(description)).encode('utf-8'),
154+
pretty_length = pretty_length.encode('utf-8'),
155+
duration = youtube_info['duration_str'].encode('utf-8'),
154156
**conf)
155157

156158
item_dict['enclosure'] = PyRSS2Gen.Enclosure(url=urlparse.urljoin(conf['url_base'], base_filename),
@@ -172,11 +174,12 @@
172174
## Write output to a file
173175
data = StringIO.StringIO()
174176
rss.write_xml(data)
175-
output_filename = "output/{org}_{course}_{url_name}_{format}.rss".format(org = conf['course_org'],
177+
output_filename = "{output_dir}/{org}_{course}_{url_name}_{format}.rss".format(org = conf['course_org'],
176178
course = conf['course_number'],
177179
url_name = conf['course_id'],
178-
format = video_format)
180+
format = video_format,
181+
output_dir = conf['output_dir'])
179182
f = open(output_filename, "w")
180-
f.write(xml.dom.minidom.parseString(data.getvalue()).toprettyxml())
183+
f.write(xml.dom.minidom.parseString(data.getvalue()).toprettyxml().encode('utf-8'))
181184
f.close()
182185
print "Saved ", output_filename

rss/index.html

+157-30
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,157 @@
1-
<h1> RSS feed </h1>
2-
3-
This is a prototype podcast for of the videos for {course}. Podcasting
4-
has a couple of advantages for viewing the videos. You can watch the
5-
videos from virtually any device, including your phone when
6-
commuting. In addition, if you subscribe to multiple podcasts, you can
7-
automatically view things when they are available, rather than
8-
checking each source individually.
9-
10-
As an engineering prototype, we can make no guarantees on whether this
11-
will continue to work. Whether we decide to launch it in other courses
12-
will be based, in part, on your experience and feedback.
13-
14-
If you need them, we have included instructions for how to install
15-
this podcast on several devices. We're not endorsing the particular
16-
applications; they just happened to be the first ones that worked for
17-
us.
18-
19-
<h2> Subscribing on iPhone and iPad </h2>
20-
21-
To subscribe to this podcast on iPad:
22-
23-
<ul>
24-
<li>
25-
Install <a href="https://itunes.apple.com/us/app/rssradio-podcast-downloader/id386600664">RSS
26-
Radio</a> on your device
27-
(there's <a href="https://itunes.apple.com/us/app/rssradio-classic/id382552897">a
28-
version for older devices as well</a>.
29-
<li>
30-
</ul>
1+
2+
<!DOCTYPE html>
3+
<html lang="en">
4+
<head>
5+
<meta charset="utf-8">
6+
<title>Positive Behavior Support for Young
7+
Children Podcast</title>
8+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
9+
<meta name="description" content="">
10+
<meta name="author" content="">
11+
12+
<!-- CSS -->
13+
<link rel="stylesheet" href="http://netdna.bootstrapcdn.com/bootstrap/3.1.1/css/bootstrap.min.css">
14+
<style type="text/css">
15+
/* Custom page CSS
16+
-------------------------------------------------- */
17+
/* Not required for template or sticky footer method. */
18+
html,
19+
body {
20+
height: 100%;
21+
/* The html and body elements cannot have any padding or margin. */
22+
}
23+
24+
/* Wrapper for page content to push down footer */
25+
#wrap {
26+
min-height: 100%;
27+
height: auto !important;
28+
height: 100%;
29+
/* Negative indent footer by it's height */
30+
margin: 0 auto -60px;
31+
}
32+
33+
/* Set the fixed height of the footer here */
34+
#push,
35+
#footer {
36+
height: 60px;
37+
}
38+
//#footer {
39+
// background-color: #f5f5f5;
40+
//}
41+
42+
/* Lastly, apply responsive CSS fixes as necessary */
43+
@media (max-width: 767px) {
44+
#footer {
45+
margin-left: -20px;
46+
margin-right: -20px;
47+
padding-left: 20px;
48+
padding-right: 20px;
49+
}
50+
}
51+
52+
.container {
53+
width: auto;
54+
max-width: 680px;
55+
}
56+
.container .credit {
57+
margin: 20px 0;
58+
}
59+
body {
60+
background-image: url("X.svg");
61+
background-repeat: no-repeat;
62+
background-size: 200% 200%;
63+
background-position: 75% 75%;
64+
}
65+
</style>
66+
</head>
67+
68+
<body>
69+
70+
71+
<!-- Part 1: Wrap all page content here -->
72+
<div id="wrap">
73+
74+
<!-- Begin page content -->
75+
<div class="container">
76+
<div class="page-header">
77+
<h1>Podcast for ECFS312X on edX</h1>
78+
79+
<p> This is a prototype podcast for of the videos for
80+
<b>Positive Behavior Support for Young Children</b> on
81+
edX. This podcast lets you:
82+
<ul>
83+
<li> Watch from virtually any device, including your
84+
phone
85+
<li> Automatically get new videos as they become
86+
available
87+
<li> Download overnight on low-bandwidth connections
88+
<li> Use your favorite RSS client
89+
</ul>
90+
<p> edX courses interleave rich assessments, discussions,
91+
and other interactive materials with video content. Some of
92+
the videos may be out-of-context without those. If you would
93+
like the full course experience, please visit the course
94+
on <a href="https://www.edx.org/course/uwashingtonx/uwashingtonx-ecfs312x-positive-behavior-1642">edx.org</a>.
95+
</p>
96+
97+
<h3> This is an engineering prototype.</h3>
98+
<p> We make no guarantees on whether this will work
99+
correctly, or continue to work correctly. You are the first
100+
testers. Whether we decide to launch it in other courses
101+
will be based, in part, on your experience and feedback. If
102+
you have any feedback about this feed, please let us know at
103+
104+
105+
<h3> Getting started </h3>
106+
<p> Used podcasts before? Simply subscribe to: </p>
107+
<pre>http://add-link-when-and-if-course-staff-approves/feed.rss</pre>
108+
109+
<p> Need a bit more help? We have instructions for a few
110+
different devices below (we're not endorsing the particular
111+
apps; they just happened to be the first ones that worked
112+
for us):
113+
114+
<h3> Subscribing on iPhone and iPad </h3>
115+
116+
To subscribe to this podcast on iPad:
117+
118+
<ul>
119+
<li>
120+
Install <a href="https://itunes.apple.com/us/app/rssradio-podcast-downloader/id386600664">RSS
121+
Radio</a> on your device
122+
(there's <a href="https://itunes.apple.com/us/app/rssradio-classic/id382552897">a
123+
version for older devices as well</a>).
124+
<li> Skip the startup screens, and at the main screen,
125+
click on the plus on the top right to add a feed. Hit
126+
"more" on the top left, and select "Enter the URL
127+
Manually." Cut-and-paste this link:
128+
<pre>http://add-link-when-and-if-course-staff-approves/feed.rss</pre>
129+
<b>Make sure to hit "subscribe" on the last page. </b>
130+
<li> Click on the feed. Select "settings" on the top right
131+
(not the app settings, but the feed settings), and change
132+
"Download" to "all."
133+
<li> Congratulations! You're done! Download and watch a few videos, and enjoy!
134+
</ul>
135+
<h3> Subscribing on Android </h3>
136+
<ul>
137+
<li> Install <a href="https://play.google.com/store/apps/details?id=com.snoggdoggler.android.applications.doggcatcher.premium&hl=en">DoggCatcher</a>.
138+
<li> On the startup screen, change downloads to 5.
139+
<li> Hit "Subscribe", and from the list, select "Feed RSS URL"
140+
<li> Scroll down, and enter the URL:
141+
<pre>http://add-link-when-and-if-course-staff-approves/feed.rss</pre>
142+
<li> On the main screen, delete all existing feeds. DoggCatcher comes with a bunch, and they'll just waste space and make clutter.
143+
<li> Go into the main feed. Hit the download button to download a few videos. Wait for a few videos to download, and hit play.
144+
</ul>
145+
</div>
146+
</div>
147+
</div>
148+
<div id="footer">
149+
<div class="container" style="text-align:right">
150+
<img src="logo.png">
151+
</div>
152+
</div>
153+
<!-- Le javascript
154+
================================================== -->
155+
<!-- Placed at the end of the document so the pages load faster -->
156+
</body>
157+
</html>

0 commit comments

Comments
 (0)