github-to-canvas 0.1.1 → 0.1.7.pre
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +121 -94
- data/bin/github-to-canvas +120 -17
- data/lib/github-to-canvas.rb +94 -9
- data/lib/github-to-canvas/canvas_interface.rb +103 -26
- data/lib/github-to-canvas/github_interface.rb +1 -1
- data/lib/github-to-canvas/repository_converter.rb +87 -19
- data/lib/github-to-canvas/version.rb +1 -1
- metadata +4 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 53c9c96308fae1d78f27bc68e80fee55baf2ff23d381866edfd1d114b30601ec
|
4
|
+
data.tar.gz: bf7570a5be4657e102659454295beb72023864ceb20562b51858fa094997b5f7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: adbf272b0121b81582d6520bc0c3881980276ede161cb97515d1792563cb29a603c93e7bf35790db1eacc89fb471b68127dccaf24fa43f3205cd758fa53739c2
|
7
|
+
data.tar.gz: 45182fbe2c428468ed967650a5e42ad496af4fa8dca236b61ec52ef9aa0ace3c209ece9a52eadd46849252a7dd6f9fa7c047a5017057bb96ca5143ee3155784a
|
data/README.md
CHANGED
@@ -6,7 +6,7 @@ The `github-to-canvas` gem is designed to aid in integrating GitHub and the
|
|
6
6
|
Canvas LMS. This gem takes a GitHub repository's `README.md` file, converts it
|
7
7
|
to HTML, and pushes it to Canvas using the Canvas API. This gem can also update
|
8
8
|
existing Canvas lessons, allowing continuous alignment of content between GitHub
|
9
|
-
and Canvas
|
9
|
+
and Canvas.
|
10
10
|
|
11
11
|
This gem is built for use internally at [Flatiron School][], so some features may be
|
12
12
|
specific to Flatiron School branding and needs. Access to the
|
@@ -63,44 +63,21 @@ are present by running `ENV` and finding them in the output list.
|
|
63
63
|
|
64
64
|
## Common Uses
|
65
65
|
|
66
|
-
|
66
|
+
The GitHub to Canvas gem can be used for the following:
|
67
67
|
|
68
|
-
|
68
|
+
- [Create a Canvas Lesson from a Local Repository](#create)
|
69
|
+
- [Create a Canvas Lesson from a Remote Repository](#createremote)
|
70
|
+
- [Read a Remote Repository as HTML](#read)
|
71
|
+
- [Update a Canvas Lesson from a Local Repository](#update)
|
72
|
+
- [Update a Canvas Lesson from a Remote Repository](#updateremote)
|
73
|
+
- [Retrieve Canvas Course Information as YAML Markdown](#query)
|
74
|
+
- [Map GitHub Repositories to a Canvas Course YAML file](#map)
|
75
|
+
- [Create New Canvas Course from a YAML file](#buildcourse)
|
76
|
+
- [Update Lessons in an Existing Course from a YAML file](#updatecourse)
|
69
77
|
|
70
|
-
|
71
|
-
lesson, converting the local repository's README.md to HTML. Adds `.canvas`
|
72
|
-
file to remote repository
|
73
|
-
- `--align`, `-a`: Updates a canvas lesson based on the local repository's
|
74
|
-
README.md. If no other options are used, `--align` will look for a `.canvas`
|
75
|
-
file to know what to lesson to update
|
76
|
-
- `--course`: Provide a specific course ID. When used with `--id`, this can
|
77
|
-
override the default behavior for `--align`, allowing you to update any
|
78
|
-
existing lesson and ignore the `.canvas` file if present.
|
79
|
-
- `--id`: Provide a specific lesson ID. This can be found in the URL of the
|
80
|
-
specific lesson. For Pages, used the slugified lesson title.
|
81
|
-
- `--type`: Sets the type of Canvas lesson to be created (page, assignment or
|
82
|
-
discussion). If no type, type decided based on repository structure.
|
83
|
-
- `--name`: Can be used to override default naming behavior. By default, Canvas
|
84
|
-
lesson names are determined by the first top-level (`#`) header in the
|
85
|
-
repository's markdown file.
|
86
|
-
- `--fis-links`, `-l`: Adds additional Flatiron School HTML header after
|
87
|
-
markdown conversion, including links back to the GitHub repo and it's issue
|
88
|
-
form.
|
89
|
-
- `--forkable`: Adds a **Fork** button to the Flatiron School HTML header. For
|
90
|
-
use with custom Canvas JS to enable Canvas assignment forking workflow for
|
91
|
-
Flatiron School students.
|
92
|
-
- `--remove-header-and-footer`, `-r`: Removes top lesson header and any Flatiron
|
93
|
-
School specific footer links before converting to HTML. Removing top lesson
|
94
|
-
header prevent duplicate titles while viewing in Canvas.
|
95
|
-
- `--create-from-github`: Requires a GitHub repository URL. Also requires
|
96
|
-
`--course` and `--type`. Creates a Canvas lesson, reading from the remote repo
|
97
|
-
instead of a local repository. Repository must be public.
|
98
|
-
- `--align-from-github`: Requires a GitHub repo URL, `--course`, `--id`, and
|
99
|
-
`--type`. Updates a Canvas lesson from a remote repository.
|
100
|
-
- `--read-from-github`: Requires a GitHub repo URL. Reads a remote repository
|
101
|
-
and converts its contents to HTML, but does not push to Canvas.
|
78
|
+
### Creating and Updating Canvas Lessons
|
102
79
|
|
103
|
-
#### Create a Canvas Lesson from a Local Repository
|
80
|
+
#### Create a Canvas Lesson from a Local Repository <a name="create"></a>
|
104
81
|
|
105
82
|
Navigate into a repository folder cloned down to your local machine and run:
|
106
83
|
|
@@ -114,16 +91,15 @@ branded footers. It will also add an HTML header for Canvas that includes links
|
|
114
91
|
back to the repository.
|
115
92
|
|
116
93
|
If the lesson type is an assignment, a Fork button will also be added to the
|
117
|
-
HTML header. Because the command didn't specify, the type of lesson is
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
ignored.
|
94
|
+
HTML header. Because the command didn't specify, the type of lesson is determined
|
95
|
+
based on the local repo structure - if it has sub-folders, the lesson will become
|
96
|
+
an assignment; if there are no sub-folders, the lesson will become a page. If the
|
97
|
+
lesson type is a page, the `--forkable` option will be ignored.
|
122
98
|
|
123
99
|
Creating a lesson this way will also produce a `.canvas` file. This file
|
124
100
|
contains info about the Canvas lesson that was created.
|
125
101
|
|
126
|
-
#### Create a Canvas Lesson from a Remote Repository
|
102
|
+
#### Create a Canvas Lesson from a Remote Repository <a name="createremote"></a>
|
127
103
|
|
128
104
|
To create from a remote repo, run the following command:
|
129
105
|
|
@@ -139,7 +115,7 @@ lesson the same way as before.
|
|
139
115
|
|
140
116
|
The repository must be public in order to read the markdown file.
|
141
117
|
|
142
|
-
#### Read a Remote Repository as HTML
|
118
|
+
#### Read a Remote Repository as HTML <a name="read"></a>
|
143
119
|
|
144
120
|
To read the contents of a remote repo:
|
145
121
|
|
@@ -150,11 +126,11 @@ github-to-canvas --read-from-github <URL>
|
|
150
126
|
This will produce an HTML conversion of the repository's markdwon. This HTML can
|
151
127
|
be directly pasted into Canvas' HTML editor if a manual process is needed.
|
152
128
|
|
153
|
-
#### Update a Canvas Lesson from a Local Repository
|
129
|
+
#### Update a Canvas Lesson from a Local Repository <a name="update"></a>
|
154
130
|
|
155
131
|
If you previously created a Canvas lesson from a local repository, you should
|
156
132
|
have a `.canvas` file present in the repo. If that file is present, you can run
|
157
|
-
the following command to
|
133
|
+
the following command to update the listed Canvas lesson automatically:
|
158
134
|
|
159
135
|
```sh
|
160
136
|
github-to-canvas -a -lr --forkable
|
@@ -169,7 +145,7 @@ github-to-canvas -a --course <CANVAS_COURSE_ID> --id <CANVAS_LESSON_ID> -lr --fo
|
|
169
145
|
|
170
146
|
Canvas course and lesson IDs can be found in the URL.
|
171
147
|
|
172
|
-
#### Update a Canvas Lesson from a Remote Repository
|
148
|
+
#### Update a Canvas Lesson from a Remote Repository <a name="updateremote"></a>
|
173
149
|
|
174
150
|
You can update an existing Canvas course using a remote GitHub repository like so:
|
175
151
|
|
@@ -182,7 +158,7 @@ using the info provided. Type must match the existing lesson type.
|
|
182
158
|
|
183
159
|
### Course Creation
|
184
160
|
|
185
|
-
This gem
|
161
|
+
This gem can create Canvas courses from scratch. These features
|
186
162
|
are still in development and may not work for all course designs. Quiz and
|
187
163
|
Discussion Topic lesson creation is still under development and will not work.
|
188
164
|
|
@@ -191,7 +167,7 @@ course, add in modules, and populate those modules with pages and assignments.
|
|
191
167
|
The required YAML file must follow a specific structure. Using the steps below,
|
192
168
|
this gem can create the necessary YAML markup from existing Canvas courses.
|
193
169
|
|
194
|
-
#### Retrieve Canvas Course Information as YAML
|
170
|
+
#### Retrieve Canvas Course Information as YAML Markdown <a name="query"></a>
|
195
171
|
|
196
172
|
To create YAML markup of an existing Canvas course, use the following:
|
197
173
|
|
@@ -243,7 +219,7 @@ The output will look similar to this:
|
|
243
219
|
|
244
220
|
The output YAML will not include associated GitHub repository information.
|
245
221
|
|
246
|
-
#### Map GitHub Repositories to a Canvas Course YAML
|
222
|
+
#### Map GitHub Repositories to a Canvas Course YAML file <a name="map"></a>
|
247
223
|
|
248
224
|
To associate repositories to an existing course YAML, the following command can be used:
|
249
225
|
|
@@ -264,7 +240,7 @@ github-to-canvas --map query_results.yml > course_structure.yml
|
|
264
240
|
The resulting YAML file will contain course information, the module and lesson
|
265
241
|
structure, and each lesson's associated GitHub repository.
|
266
242
|
|
267
|
-
#### Create New Canvas Course from YAML
|
243
|
+
#### Create New Canvas Course from a YAML file <a name="buildcourse"></a>
|
268
244
|
|
269
245
|
To create a Canvas course with this gem, you will need a correctly structured
|
270
246
|
YAML file with the course info, modules, lessons and associated lesson
|
@@ -288,18 +264,18 @@ This command will cause the following to happen:
|
|
288
264
|
- Create the second lesson and add it to the module...
|
289
265
|
- Repeate process until all modules and lessons are created
|
290
266
|
|
291
|
-
####
|
267
|
+
#### Update Lessons in an Existing Course from a YAML file <a name="updatecourse"></a>
|
292
268
|
|
293
|
-
|
294
|
-
|
295
|
-
|
269
|
+
The GitHub to Canvas gem can be used to update all lessons in a course
|
270
|
+
with a single command. To do this, you will need an up-to-date course YAML file with repositories
|
271
|
+
mapped to each lesson.
|
296
272
|
|
297
273
|
```sh
|
298
274
|
github-to-canvas --query <COURSE_ID> > query_results.yml
|
299
275
|
github-to-canvas --map query_results.yml > your_new_course.yml
|
300
276
|
```
|
301
277
|
|
302
|
-
|
278
|
+
Use the resulting file (in this example, `your_new_course.yml`) to update all lessons in
|
303
279
|
a course based on their GitHub repo:
|
304
280
|
|
305
281
|
```sh
|
@@ -314,33 +290,49 @@ associated repository.
|
|
314
290
|
### HTML Code Snippets Do Not Render
|
315
291
|
|
316
292
|
The Canvas API renders all HTML it receives. If your repository's markdown
|
317
|
-
includes HTML that is not meant to be rendered
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
To
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
293
|
+
includes HTML that is not meant to be rendered, the content will be rendered as
|
294
|
+
part of the page's HTML, resulting in unusual display errors in Canvas. Examples of
|
295
|
+
this would be lessons on HTML or JavaScript that include HTML code snippets.
|
296
|
+
|
297
|
+
To prevent HTML from being rendered, include the `--contains-html` option when
|
298
|
+
running the GitHub to Canvas gem. This replaces `<` and `>` characters with HTML
|
299
|
+
charset values wrapped in `span` elements. This will stop Canvas from rendering
|
300
|
+
the HTML.
|
301
|
+
|
302
|
+
If your markdown contains a mix of HTML that should and should not be rendered,
|
303
|
+
you will need to either replace HTML that you want to be rendered with markdown
|
304
|
+
syntax equivalents. For example, HTML you want to display as code and an `<img>`
|
305
|
+
element you want to render as the image itself, replace the `<img>` tag with
|
306
|
+
markdown syntax (``).
|
307
|
+
|
308
|
+
The one exception is the `<iframe>` element. There is no way to easily embed
|
309
|
+
videos in GitHub markdown without HTML, so this tag will always be allowed to
|
310
|
+
render in Canvas, whether or not you use `--contains-html`.
|
311
|
+
|
312
|
+
If you have HTML related rendering issues in Canvas that can't be fixed with
|
313
|
+
`--contains-html`:
|
314
|
+
|
315
|
+
- Go to the Canvas WYSIWYG editor for the afflicted lesson.
|
316
|
+
- Click the HTML editor option (`</>` button in the lower right) to switch to
|
317
|
+
HTML.
|
318
|
+
- Read the GitHub repo as HTML:
|
319
|
+
|
320
|
+
```sh
|
321
|
+
github-to-canvas --read-from-github URL
|
322
|
+
```
|
323
|
+
|
324
|
+
- Copy the output HTML and paste it in to the Canvas editor. This should clear up
|
325
|
+
some larger page rendering issues, but may not fix all code snippets issues.
|
326
|
+
- Switch back to the regular Canvas WYSIWYG editor
|
327
|
+
- Open a second tab to the GitHub repo you're converting from.
|
328
|
+
- Copy any HTML code snippets from GitHub and paste them into the Canvas editor
|
329
|
+
where they should be displayed.
|
336
330
|
|
337
331
|
The Canvas editor will treat the pasted HTML content as code and will
|
338
332
|
automatically replace some characters, escaping the code from the
|
339
333
|
normal rendering process.
|
340
334
|
|
341
|
-
Note that realigning after fixing this content
|
342
|
-
rendering for these lessons again. A fix is planned for this issue, but has not
|
343
|
-
been implemented.
|
335
|
+
Note that realigning after fixing this content may overwrite fixes.
|
344
336
|
|
345
337
|
### Multi-Line Code Snippets Render as a Single Line
|
346
338
|
|
@@ -352,9 +344,17 @@ issues, please open a new issue with a markdown example to replicate the error.
|
|
352
344
|
|
353
345
|
### Markdown Formatting Issues Cause Errors in Canvas
|
354
346
|
|
355
|
-
|
356
|
-
|
357
|
-
|
347
|
+
An empty line should separate individual markdown headers, paragraphs and code snippets
|
348
|
+
in the markdown. Without these empty lines, the contents will be interpretted as one
|
349
|
+
continuous paragraph and ignore formatting.
|
350
|
+
|
351
|
+
### New Repos That Use a `main` Branch
|
352
|
+
|
353
|
+
If you are using a new GitHub repository that uses a `main` branch, you may not be able to
|
354
|
+
create or update from a remote repository. You can still create and update from a local
|
355
|
+
repository by using the `--branch` (`-b`) option and specifying `main` as the branch.
|
356
|
+
|
357
|
+
A fix is planned for this issue, but not implemented.
|
358
358
|
|
359
359
|
## Overview of GitHub to Canvas workflows
|
360
360
|
|
@@ -362,9 +362,8 @@ Using this gem, you can maintain Canvas courses in multiple ways - either by
|
|
362
362
|
creating and updating individual Canvas lessons or through the YAML file process.
|
363
363
|
|
364
364
|
At Flatiron School, we use the Canvas blueprint course feature. We use the
|
365
|
-
github-to-canvas gem to update the blueprint copy of lessons. These updates will
|
366
|
-
|
367
|
-
copies.
|
365
|
+
github-to-canvas gem to update the blueprint copy of lessons. These updates will appear
|
366
|
+
in future copies of the course or when synced down associated courses in Canvas.
|
368
367
|
|
369
368
|

|
370
369
|
|
@@ -375,18 +374,46 @@ If you are using github-to-canvas in this way, changes should always be made on
|
|
375
374
|
the repository, not on Canvas. Any changes made only on Canvas will get
|
376
375
|
overwritten if the lesson is updated using the gem.
|
377
376
|
|
378
|
-
##
|
379
|
-
|
380
|
-
### Specify Branch
|
377
|
+
## Common Options
|
381
378
|
|
382
|
-
|
383
|
-
|
384
|
-
|
385
|
-
|
386
|
-
If
|
387
|
-
|
388
|
-
|
389
|
-
|
379
|
+
- `--create-lesson`, `-c`: Requires a Canvas course ID. Creates a new Canvas
|
380
|
+
lesson, converting the local repository's README.md to HTML. Adds `.canvas`
|
381
|
+
file to remote repository
|
382
|
+
- `--align`, `-a`: Updates a canvas lesson based on the local repository's
|
383
|
+
README.md. If no other options are used, `--align` will look for a `.canvas`
|
384
|
+
file to know what to lesson to update
|
385
|
+
- `--course`: Provide a specific course ID. When used with `--id`, this can
|
386
|
+
override the default behavior for `--align`, allowing you to update any
|
387
|
+
existing lesson and ignore the `.canvas` file if present.
|
388
|
+
- `--id`: Provide a specific lesson ID. This can be found in the URL of the
|
389
|
+
specific lesson. For Pages, used the slugified lesson title.
|
390
|
+
- `--type`: Sets the type of Canvas lesson to be created (page, assignment or
|
391
|
+
discussion). If no type, type decided based on repository structure.
|
392
|
+
- `--name`: Can be used to override default naming behavior. By default, Canvas
|
393
|
+
lesson names are determined by the first top-level (`#`) header in the
|
394
|
+
repository's markdown file.
|
395
|
+
- `--fis-links`, `-l`: Adds additional Flatiron School HTML header after
|
396
|
+
markdown conversion, including links back to the GitHub repo and it's issue
|
397
|
+
form.
|
398
|
+
- `--forkable`: Adds a **Fork** button to the Flatiron School HTML header. For
|
399
|
+
use with custom Canvas JS to enable Canvas assignment forking workflow for
|
400
|
+
Flatiron School students.
|
401
|
+
- `--remove-header-and-footer`, `-r`: Removes top lesson header and any Flatiron
|
402
|
+
School specific footer links before converting to HTML. Removing top lesson
|
403
|
+
header prevents duplicate titles while viewing in Canvas.
|
404
|
+
- `--create-from-github`: Requires a GitHub repository URL. Also requires
|
405
|
+
`--course` and `--type`. Creates a Canvas lesson, reading from the remote repo
|
406
|
+
instead of a local repository. Repository must be public.
|
407
|
+
- `--align-from-github`: Requires a GitHub repo URL, `--course`, `--id`, and
|
408
|
+
`--type`. Updates a Canvas lesson from a remote repository.
|
409
|
+
- `--read-from-github`: Requires a GitHub repo URL. Reads a remote repository
|
410
|
+
and converts its contents to HTML, but does not push to Canvas.
|
411
|
+
- `--branch`, `-b`: Can be used when creating or aligning with a local repo to
|
412
|
+
specify which branch to use. Use this if you have a new repository that uses a
|
413
|
+
`main` branch instead of `master`.
|
414
|
+
- `--save-to-github`, `-s`: If you are are creating or aligning content locally,
|
415
|
+
the `.canvas` file is not automatically committed to the repo. This option will
|
416
|
+
attempt to commit and push the `.canvas` file to the remote repository.
|
390
417
|
|
391
418
|
Run `github-to-canvas --help` for additional options not listed in this Readme.
|
392
419
|
|
data/bin/github-to-canvas
CHANGED
@@ -1,4 +1,7 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'byebug'
|
4
|
+
|
2
5
|
require 'optparse'
|
3
6
|
require 'github-to-canvas'
|
4
7
|
|
@@ -75,12 +78,15 @@ OptionParser.new do |opts|
|
|
75
78
|
end
|
76
79
|
opts.on("-tTYPE", "--type TYPE",
|
77
80
|
"Sets the type Canvas lesson to be created (page or assignment). If no type, type decided based on repository structure") do |type|
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
81
|
+
# byebug
|
82
|
+
options[:type] = type.downcase
|
83
|
+
abort if type == 'quiz' || type == 'discussion'
|
84
|
+
# if type == 'page' || type == 'assignment' || type == 'discussion' || type == 'quiz' || type == 'Page' || type == 'Assignment' || type == 'Discussion' || type == 'Quiz'
|
85
|
+
|
86
|
+
# else
|
87
|
+
# puts "Invalid type. Defaulting to page"
|
88
|
+
# options[:type] = "page"
|
89
|
+
# end
|
84
90
|
end
|
85
91
|
opts.on("-fFILE", "--file FILE",
|
86
92
|
"Looks for and uses a markdown file in the currentt folder as source for conversion. Default file is README.md. Skips writing .canvas to repository") do |file|
|
@@ -102,6 +108,14 @@ OptionParser.new do |opts|
|
|
102
108
|
"Adds additional Flatiron School HTML after markdown conversion") do |f|
|
103
109
|
options[:fis] = true
|
104
110
|
end
|
111
|
+
opts.on("-g", "--git-links",
|
112
|
+
"Adds additional GitHub after markdown conversion") do |f|
|
113
|
+
options[:git_links] = true
|
114
|
+
end
|
115
|
+
opts.on("--aaq",
|
116
|
+
"Adds AAQ flag to HTML header appended with --fis-links") do |aaq|
|
117
|
+
options[:aaq] = aaq
|
118
|
+
end
|
105
119
|
opts.on("--forkable",
|
106
120
|
"Used with --fis-links, adds fork button to HTML header injected into Canvas lesson") do |remote|
|
107
121
|
options[:forkable] = true
|
@@ -130,6 +144,14 @@ OptionParser.new do |opts|
|
|
130
144
|
"REQUIRES -f or --file Associates canvas lessons with repositories. Use query to create required YAML file") do |file|
|
131
145
|
options[:map] = file
|
132
146
|
end
|
147
|
+
opts.on("--urls-only",
|
148
|
+
"Use with --map. Outputs repo URLs instead of YAML") do |urls|
|
149
|
+
options[:urls_only] = urls
|
150
|
+
end
|
151
|
+
# opts.on("--csv COURSE",
|
152
|
+
# "Returns a course's lesson struction as CSV") do |course|
|
153
|
+
# options[:csv] = course
|
154
|
+
# end
|
133
155
|
opts.on("--read-from-canvas CANVAS_URL",
|
134
156
|
"Retrieves an existing Canvas lesson using the provided URL") do |url|
|
135
157
|
options[:read_from_canvas] = url
|
@@ -162,6 +184,23 @@ OptionParser.new do |opts|
|
|
162
184
|
"Updates all lessons in a course using remote repos in provided YAML file") do |file|
|
163
185
|
options[:update_course_lessons] = file
|
164
186
|
end
|
187
|
+
opts.on("--clone-from-yaml YAML_FILE",
|
188
|
+
"Iterates over provided course YAML file and clones repos locally") do |file|
|
189
|
+
options[:clone_from_yaml] = file
|
190
|
+
end
|
191
|
+
opts.on("--contains-html",
|
192
|
+
"Escapes all HTML included in source markdown by replacing '<' and '>' with HTML charset values") do |html|
|
193
|
+
options[:contains_html] = html
|
194
|
+
end
|
195
|
+
opts.on("--canvas-to-canvas COURSE",
|
196
|
+
"Copies an existing Canvas lesson into another Canvas lesson") do |canvas_to_canvas|
|
197
|
+
options[:canvas_to_canvas] = canvas_to_canvas
|
198
|
+
end
|
199
|
+
opts.on("--build-from-csv CSV",
|
200
|
+
"Build a course usin a CSV of lesson repos, names, modules, and types") do |csv_build|
|
201
|
+
options[:csv_build] = csv_build
|
202
|
+
end
|
203
|
+
|
165
204
|
|
166
205
|
end.parse!
|
167
206
|
|
@@ -171,12 +210,29 @@ if options[:version]
|
|
171
210
|
end
|
172
211
|
|
173
212
|
if options[:read_from_canvas]
|
174
|
-
GithubToCanvas.new(mode: 'canvas_read',
|
213
|
+
GithubToCanvas.new(mode: 'canvas_read',
|
214
|
+
filepath: options[:read_from_canvas])
|
215
|
+
abort
|
216
|
+
end
|
217
|
+
|
218
|
+
if options[:canvas_to_canvas]
|
219
|
+
GithubToCanvas.new(mode: 'canvas_copy',
|
220
|
+
filepath: options[:canvas_to_canvas],
|
221
|
+
course_id: options[:course_id],
|
222
|
+
type: options[:type],
|
223
|
+
id: options[:id]
|
224
|
+
)
|
175
225
|
abort
|
176
226
|
end
|
177
227
|
|
178
228
|
if options[:read_from_github]
|
179
|
-
GithubToCanvas.new(mode: 'github_read',
|
229
|
+
GithubToCanvas.new(mode: 'github_read',
|
230
|
+
filepath: options[:read_from_github],
|
231
|
+
remove_header_and_footer: !!options[:remove_header_and_footer],
|
232
|
+
forkable: !!options[:forkable],
|
233
|
+
fis_links: !!options[:fis],
|
234
|
+
aaq: !!options[:aaq],
|
235
|
+
contains_html: options[:contains_html])
|
180
236
|
abort
|
181
237
|
end
|
182
238
|
|
@@ -186,9 +242,12 @@ if options[:create_from_github]
|
|
186
242
|
filepath: options[:create_from_github],
|
187
243
|
course_id: options[:course_id],
|
188
244
|
type: options[:type],
|
245
|
+
name: options[:name],
|
189
246
|
remove_header_and_footer: !!options[:remove_header_and_footer],
|
190
247
|
forkable: !!options[:forkable],
|
191
|
-
fis_links: !!options[:fis]
|
248
|
+
fis_links: !!options[:fis],
|
249
|
+
aaq: !!options[:aaq],
|
250
|
+
contains_html: options[:contains_html])
|
192
251
|
else
|
193
252
|
puts 'Canvas course ID and lesson type required. Example: github-to-canvas --create-from-github URL --course ID --type TYPE'
|
194
253
|
end
|
@@ -202,9 +261,12 @@ if options[:align_from_github]
|
|
202
261
|
course_id: options[:course_id],
|
203
262
|
type: options[:type],
|
204
263
|
id: options[:id],
|
264
|
+
name: options[:name],
|
205
265
|
remove_header_and_footer: !!options[:remove_header_and_footer],
|
206
266
|
forkable: !!options[:forkable],
|
207
|
-
fis_links: !!options[:fis]
|
267
|
+
fis_links: !!options[:fis],
|
268
|
+
aaq: !!options[:aaq],
|
269
|
+
contains_html: options[:contains_html])
|
208
270
|
else
|
209
271
|
puts 'Canvas course ID, lesson ID, and type required. Example: github-to-canvas --create-from-github URL --course COURSE_ID --id LESSON_ID --type TYPE'
|
210
272
|
end
|
@@ -217,7 +279,28 @@ if options[:query]
|
|
217
279
|
end
|
218
280
|
|
219
281
|
if options[:map]
|
220
|
-
GithubToCanvas.new(mode: 'map',
|
282
|
+
GithubToCanvas.new(mode: 'map',
|
283
|
+
file_to_convert: options[:map],
|
284
|
+
urls_only: !!options[:urls_only])
|
285
|
+
abort
|
286
|
+
end
|
287
|
+
|
288
|
+
if options[:csv]
|
289
|
+
GithubToCanvas.new(mode: 'csv', file_to_convert: options[:csv])
|
290
|
+
abort
|
291
|
+
end
|
292
|
+
|
293
|
+
if options[:csv_build]
|
294
|
+
GithubToCanvas.new(mode: 'csv_build',
|
295
|
+
file_to_convert: options[:csv_build],
|
296
|
+
course_id: options[:course_id],
|
297
|
+
fis_links: !!options[:fis],
|
298
|
+
remove_header_and_footer: !!options[:remove_header_and_footer],
|
299
|
+
aaq: !!options[:aaq],
|
300
|
+
forkable: !!options[:forkable],
|
301
|
+
branch: options[:branch],
|
302
|
+
contains_html: options[:contains_html],
|
303
|
+
git_links: !!options[:git_links])
|
221
304
|
abort
|
222
305
|
end
|
223
306
|
|
@@ -226,7 +309,10 @@ if options[:build_course]
|
|
226
309
|
file_to_convert: options[:build_course],
|
227
310
|
fis_links: !!options[:fis],
|
228
311
|
remove_header_and_footer: !!options[:remove_header_and_footer],
|
229
|
-
|
312
|
+
aaq: !!options[:aaq],
|
313
|
+
forkable: !!options[:forkable],
|
314
|
+
contains_html: options[:contains_html],
|
315
|
+
git_links: !!options[:git_links])
|
230
316
|
abort
|
231
317
|
end
|
232
318
|
|
@@ -237,7 +323,10 @@ if options[:add_to_course]
|
|
237
323
|
file_to_convert: options[:add_to_course],
|
238
324
|
fis_links: !!options[:fis],
|
239
325
|
remove_header_and_footer: !!options[:remove_header_and_footer],
|
240
|
-
forkable: !!options[:forkable]
|
326
|
+
forkable: !!options[:forkable],
|
327
|
+
aaq: !!options[:aaq],
|
328
|
+
contains_html: options[:contains_html],
|
329
|
+
git_links: !!options[:git_links])
|
241
330
|
else
|
242
331
|
puts '--course required'
|
243
332
|
end
|
@@ -249,10 +338,18 @@ if options[:update_course_lessons]
|
|
249
338
|
file_to_convert: options[:update_course_lessons],
|
250
339
|
fis_links: !!options[:fis],
|
251
340
|
remove_header_and_footer: !!options[:remove_header_and_footer],
|
252
|
-
forkable: !!options[:forkable]
|
341
|
+
forkable: !!options[:forkable],
|
342
|
+
aaq: !!options[:aaq],
|
343
|
+
contains_html: options[:contains_html],
|
344
|
+
git_links: !!options[:git_links])
|
253
345
|
abort
|
254
346
|
end
|
255
347
|
|
348
|
+
if options[:clone_from_yaml]
|
349
|
+
GithubToCanvas.new(mode: 'clone_course',
|
350
|
+
file_to_convert: options[:clone_from_yaml])
|
351
|
+
abort
|
352
|
+
end
|
256
353
|
|
257
354
|
if !options[:type]
|
258
355
|
if Dir.glob("**/*/").empty?
|
@@ -304,9 +401,12 @@ if options[:create_lesson]
|
|
304
401
|
type: options[:type],
|
305
402
|
save_to_github: !!options[:save_to_github],
|
306
403
|
fis_links: !!options[:fis],
|
404
|
+
git_links: !!options[:git_links],
|
307
405
|
remove_header_and_footer: !!options[:remove_header_and_footer],
|
308
406
|
only_update_content: !!options[:only_content],
|
309
|
-
forkable: !!options[:forkable]
|
407
|
+
forkable: !!options[:forkable],
|
408
|
+
aaq: !!options[:aaq],
|
409
|
+
contains_html: options[:contains_html])
|
310
410
|
end
|
311
411
|
|
312
412
|
if options[:align]
|
@@ -319,8 +419,11 @@ if options[:align]
|
|
319
419
|
name: options[:name],
|
320
420
|
type: options[:type],
|
321
421
|
save_to_github: !!options[:save_to_github],
|
322
|
-
fis_links: !!options[:fis],
|
422
|
+
fis_links: !!options[:fis],
|
423
|
+
git_links: !!options[:git_links],
|
323
424
|
remove_header_and_footer: !!options[:remove_header_and_footer],
|
324
425
|
only_update_content: !!options[:only_content],
|
325
|
-
forkable: !!options[:forkable]
|
426
|
+
forkable: !!options[:forkable],
|
427
|
+
aaq: !!options[:aaq],
|
428
|
+
contains_html: options[:contains_html])
|
326
429
|
end
|
data/lib/github-to-canvas.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'byebug'
|
2
|
+
require 'csv'
|
1
3
|
require_relative './github-to-canvas/create_canvas_lesson'
|
2
4
|
require_relative './github-to-canvas/update_canvas_lesson'
|
3
5
|
require_relative './github-to-canvas/canvas_dotfile'
|
@@ -20,12 +22,16 @@ class GithubToCanvas
|
|
20
22
|
when 'query'
|
21
23
|
CanvasInterface.get_course_info(options[:course_id], options[:id])
|
22
24
|
when 'map'
|
23
|
-
CanvasInterface.map_course_info(options
|
25
|
+
CanvasInterface.map_course_info(options)
|
26
|
+
when 'csv'
|
27
|
+
CanvasInterface.csv(options[:file_to_convert])
|
24
28
|
when 'canvas_read'
|
25
29
|
puts CanvasInterface.read_lesson(options[:filepath])
|
30
|
+
when 'canvas_copy'
|
31
|
+
CanvasInterface.copy_lesson(options)
|
26
32
|
when 'github_read'
|
27
|
-
|
28
|
-
puts RepositoryConverter.
|
33
|
+
html = RepositoryConverter.remote_file_conversion(options)
|
34
|
+
puts RepositoryConverter.adjust_converted_html(options, html)
|
29
35
|
when 'create' # used with a local repo
|
30
36
|
html = RepositoryConverter.local_file_conversion(options)
|
31
37
|
name = RepositoryInterface.get_name(options[:filepath], html)
|
@@ -35,21 +41,30 @@ class GithubToCanvas
|
|
35
41
|
puts "Canvas lesson created. Lesson available at #{response['html_url']}"
|
36
42
|
when 'align' # used with a local repo
|
37
43
|
html = RepositoryConverter.local_file_conversion(options)
|
38
|
-
name = RepositoryInterface.get_name(options[:filepath], html)
|
44
|
+
name = options[:name] ? options[:name] : RepositoryInterface.get_name(options[:filepath], html)
|
39
45
|
html = RepositoryConverter.adjust_converted_html(options, html)
|
40
46
|
CanvasInterface.update_all_related_lessons(options, name, html)
|
41
47
|
|
42
48
|
when 'github_create'
|
49
|
+
if (!options[:branch])
|
50
|
+
options[:branch] = 'master'
|
51
|
+
end
|
43
52
|
html = RepositoryConverter.remote_file_conversion(options)
|
44
|
-
name = RepositoryInterface.get_name(options[:filepath], html)
|
45
|
-
html = RepositoryConverter.adjust_converted_html(options, html)
|
46
53
|
|
54
|
+
html = RepositoryConverter.adjust_converted_html(options, html)
|
55
|
+
name = options[:name] ? options[:name] : RepositoryInterface.get_name(options[:filepath], html)
|
56
|
+
byebug
|
57
|
+
puts name
|
47
58
|
response = CanvasInterface.create_lesson(options, name, html)
|
48
59
|
|
49
60
|
puts "Canvas lesson created. Lesson available at #{response['html_url']}"
|
50
61
|
when 'github_align'
|
62
|
+
if (!options[:branch])
|
63
|
+
options[:branch] = 'master'
|
64
|
+
end
|
51
65
|
html = RepositoryConverter.remote_file_conversion(options)
|
52
|
-
name = RepositoryInterface.get_name(options[:filepath], html)
|
66
|
+
name = options[:name] ? options[:name] : RepositoryInterface.get_name(options[:filepath], html)
|
67
|
+
|
53
68
|
html = RepositoryConverter.adjust_converted_html(options, html)
|
54
69
|
response = CanvasInterface.update_existing_lesson(options, name, html)
|
55
70
|
puts "Canvas lesson updated. Lesson available at #{response['html_url']}"
|
@@ -69,10 +84,8 @@ class GithubToCanvas
|
|
69
84
|
options[:course_id] = created_course_info["id"]
|
70
85
|
options[:filepath] = lesson["repository"]
|
71
86
|
|
72
|
-
|
73
87
|
html = RepositoryConverter.remote_file_conversion(options)
|
74
88
|
# Add each lesson to it's module
|
75
|
-
|
76
89
|
html = RepositoryConverter.adjust_converted_html(options, html)
|
77
90
|
created_lesson_info = CanvasInterface.create_lesson(options, lesson["title"], html)
|
78
91
|
lesson = lesson.merge(created_lesson_info)
|
@@ -133,6 +146,78 @@ class GithubToCanvas
|
|
133
146
|
sleep(1)
|
134
147
|
}
|
135
148
|
}
|
149
|
+
when 'clone_course'
|
150
|
+
course_yaml = YAML.load(File.read(options[:file_to_convert]))
|
151
|
+
new_dir = "#{course_yaml[:name].downcase.gsub(' ','-')}"
|
152
|
+
cmd = "mkdir #{new_dir}"
|
153
|
+
`#{cmd}`
|
154
|
+
course_yaml[:modules].each { |module_info|
|
155
|
+
puts "Cloning #{module_info[:name]}"
|
156
|
+
module_info[:lessons].each { |lesson|
|
157
|
+
if lesson["repository"] == ""
|
158
|
+
puts "No repository found for #{lesson['title']}"
|
159
|
+
next
|
160
|
+
else
|
161
|
+
cmd = "git clone #{lesson['repository']}"
|
162
|
+
puts cmd
|
163
|
+
GithubInterface.cd_into_and(new_dir, cmd)
|
164
|
+
end
|
165
|
+
}
|
166
|
+
}
|
167
|
+
when 'csv_build'
|
168
|
+
if !options[:course_id]
|
169
|
+
course_info = {
|
170
|
+
name: "CSV Build Test",
|
171
|
+
course_code: "CSV-TEST"
|
172
|
+
}
|
173
|
+
created_course_info = CanvasInterface.create_course(course_info)
|
174
|
+
puts "Course created - #{created_course_info["id"]}"
|
175
|
+
puts "Make sure to add yourself as a teacher to this course before continuing, then press Enter/Return"
|
176
|
+
input = gets
|
177
|
+
options[:course_id] = created_course_info["id"]
|
178
|
+
else
|
179
|
+
puts "Adding to course #{options[:course_id]}"
|
180
|
+
end
|
181
|
+
|
182
|
+
csv_data = CSV.read(options[:file_to_convert])
|
183
|
+
created_module_info = {
|
184
|
+
"id" => "",
|
185
|
+
"name" => ""
|
186
|
+
}
|
187
|
+
|
188
|
+
csv_data.each { |lesson|
|
189
|
+
# lesson[0] == repo
|
190
|
+
# lesson[1] == name
|
191
|
+
# lesson[2] == module
|
192
|
+
# lesson[3] == type
|
193
|
+
# lesson[4] == yes/no contains HTML
|
194
|
+
module_info = {
|
195
|
+
name: lesson[2]
|
196
|
+
}
|
197
|
+
if created_module_info["name"] != module_info[:name]
|
198
|
+
created_module_info = CanvasInterface.create_module(options[:course_id], module_info)
|
199
|
+
puts "New module created - #{created_module_info["id"]} - #{created_module_info["name"]}"
|
200
|
+
end
|
201
|
+
|
202
|
+
options[:filepath] = lesson[0]
|
203
|
+
options[:name] = lesson[1]
|
204
|
+
options[:type] = lesson[3]
|
205
|
+
options[:branch] = "master" if !options[:branch]
|
206
|
+
if !options[:contains_html]
|
207
|
+
options[:contains_html] = (lesson[4] == "yes" || lesson[4] == "Yes") ? true : false
|
208
|
+
end
|
209
|
+
|
210
|
+
|
211
|
+
html = RepositoryConverter.remote_file_conversion(options)
|
212
|
+
html = RepositoryConverter.adjust_converted_html(options, html)
|
213
|
+
created_lesson_info = CanvasInterface.create_lesson(options, lesson[1], html)
|
214
|
+
created_lesson_info["page_url"] = created_lesson_info["url"] if !created_lesson_info["page_url"]
|
215
|
+
created_lesson_info["id"] = created_lesson_info["page_url"] if !created_lesson_info["id"]
|
216
|
+
created_lesson_info["type"] = options[:type]
|
217
|
+
puts "Creating lesson - #{options[:name]}"
|
218
|
+
response = CanvasInterface.add_to_module(options[:course_id], created_module_info, created_lesson_info)
|
219
|
+
|
220
|
+
}
|
136
221
|
else
|
137
222
|
puts VERSION
|
138
223
|
end
|
@@ -1,3 +1,4 @@
|
|
1
|
+
require 'byebug'
|
1
2
|
require 'json'
|
2
3
|
require 'rest-client'
|
3
4
|
require 'yaml'
|
@@ -46,41 +47,39 @@ class CanvasInterface
|
|
46
47
|
JSON.parse(response.body)
|
47
48
|
end
|
48
49
|
|
49
|
-
def self.create_quiz(options, quiz_data)
|
50
|
-
|
51
|
-
end
|
52
|
-
|
53
50
|
def self.add_to_module(course_id, module_info, lesson_info)
|
54
51
|
# POST /api/v1/courses/:course_id/modules/:module_id/items
|
55
52
|
url = "#{ENV['CANVAS_API_PATH']}/courses/#{course_id}/modules/#{module_info["id"]}/items"
|
56
53
|
|
57
|
-
if lesson_info["type"] == "Page"
|
54
|
+
if lesson_info["type"] == "Page" || lesson_info["type"] == "page"
|
58
55
|
payload = {
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
56
|
+
'module_item[title]' => lesson_info["title"],
|
57
|
+
'module_item[type]' => lesson_info["type"].capitalize,
|
58
|
+
'module_item[indent]' => 0,
|
59
|
+
'module_item[page_url]' => lesson_info["id"],
|
60
|
+
'module_item[completion_requirement][type]' => 'must_view'
|
61
|
+
}
|
64
62
|
elsif lesson_info["type"] == "Quiz"
|
65
63
|
puts "Quiz needs to be added manually - #{lesson_info['title']} - lesson_info["
|
66
64
|
else
|
67
65
|
|
68
66
|
payload = {
|
69
67
|
'module_item[title]' => lesson_info["title"],
|
70
|
-
'module_item[type]' => lesson_info["type"],
|
68
|
+
'module_item[type]' => lesson_info["type"].capitalize,
|
71
69
|
'module_item[indent]' => 1,
|
70
|
+
'module_item[content_id]' => lesson_info["id"],
|
72
71
|
'module_item[completion_requirement][type]' => 'must_submit'
|
73
72
|
}
|
74
73
|
end
|
75
74
|
begin
|
76
75
|
response = RestClient.post(url, payload, self.headers)
|
77
76
|
rescue
|
78
|
-
|
79
|
-
|
77
|
+
byebug
|
78
|
+
|
79
|
+
puts "Something went wrong while adding lesson #{lesson_info["id"]} to module #{module_info["id"]} in course #{course_id}" if lesson_info["type"] == "Assignment"
|
80
|
+
puts "Something went wrong while adding lesson #{lesson_info["page_url"]} to module #{module_info["id"]} in course #{course_id}" if lesson_info["type"] == "Page"
|
80
81
|
abort
|
81
82
|
end
|
82
|
-
|
83
|
-
|
84
83
|
response
|
85
84
|
|
86
85
|
end
|
@@ -95,7 +94,7 @@ class CanvasInterface
|
|
95
94
|
|
96
95
|
begin
|
97
96
|
headers = self.headers
|
98
|
-
if options[:type] == 'page'
|
97
|
+
if options[:type] == 'page' || options[:type] == 'Page'
|
99
98
|
response = RestClient.get(url, headers)
|
100
99
|
lesson_info = JSON.parse(response)
|
101
100
|
url = url.sub(/[^\/]+$/, lesson_info["page_id"].to_s)
|
@@ -170,6 +169,7 @@ class CanvasInterface
|
|
170
169
|
[info, type]
|
171
170
|
end
|
172
171
|
|
172
|
+
|
173
173
|
def self.get_course_info(course, id)
|
174
174
|
if id
|
175
175
|
lesson_data = self.get_lesson_info(course, id)
|
@@ -195,6 +195,7 @@ class CanvasInterface
|
|
195
195
|
while !!index
|
196
196
|
url = "#{ENV['CANVAS_API_PATH']}/courses/#{course}/modules?page=#{index}&per_page=20"
|
197
197
|
index += 1
|
198
|
+
|
198
199
|
response = RestClient.get(url, self.headers)
|
199
200
|
modules = JSON.parse(response.body)
|
200
201
|
|
@@ -202,7 +203,7 @@ class CanvasInterface
|
|
202
203
|
course_info[:modules] = course_info[:modules] + modules
|
203
204
|
else
|
204
205
|
index = nil
|
205
|
-
end
|
206
|
+
end
|
206
207
|
end
|
207
208
|
|
208
209
|
course_info[:modules] = course_info[:modules].map do |mod|
|
@@ -215,11 +216,12 @@ class CanvasInterface
|
|
215
216
|
while !!index
|
216
217
|
url = "#{ENV['CANVAS_API_PATH']}/courses/#{course}/modules/#{mod['id']}/items?page=#{index}&per_page=20"
|
217
218
|
index += 1
|
218
|
-
response = RestClient.get(url, headers
|
219
|
-
"Authorization" => "Bearer #{ENV['CANVAS_API_KEY']}"
|
220
|
-
})
|
219
|
+
response = RestClient.get(url, self.headers)
|
221
220
|
lessons = JSON.parse(response.body)
|
222
221
|
lessons = lessons.map do |lesson|
|
222
|
+
if lesson["type"] == "ExternalUrl"
|
223
|
+
next
|
224
|
+
end
|
223
225
|
lesson = lesson.slice("id","title","name","indent","type","html_url","page_url","url","completion_requirement", "published")
|
224
226
|
lesson["repository"] = ""
|
225
227
|
lesson['id'] = lesson['url'].gsub(/^(.*[\\\/])/,'')
|
@@ -234,16 +236,65 @@ class CanvasInterface
|
|
234
236
|
end
|
235
237
|
new_mod
|
236
238
|
end
|
237
|
-
|
239
|
+
|
238
240
|
puts course_info.to_yaml
|
239
241
|
|
240
242
|
rescue
|
243
|
+
byebug
|
241
244
|
puts "Something went wrong while getting info about course #{course}"
|
242
245
|
abort
|
243
246
|
end
|
244
247
|
end
|
245
248
|
|
246
|
-
def self.map_course_info(
|
249
|
+
def self.map_course_info(options)
|
250
|
+
course_info = YAML.load(File.read("#{Dir.pwd}/#{options[:file_to_convert]}"))
|
251
|
+
course_info[:modules] = course_info[:modules].map do |mod|
|
252
|
+
mod[:lessons] = mod[:lessons].map do |lesson|
|
253
|
+
|
254
|
+
url = lesson["url"]
|
255
|
+
response = RestClient.get(url, headers={
|
256
|
+
"Authorization" => "Bearer #{ENV['CANVAS_API_KEY']}"
|
257
|
+
})
|
258
|
+
begin
|
259
|
+
lesson_data = JSON.parse(response)
|
260
|
+
contents = lesson_data["body"] if lesson["type"] == "Page"
|
261
|
+
contents = lesson_data["message"] if lesson["type"] == "Discussion"
|
262
|
+
contents = lesson_data["description"] if lesson["type"] == "Assignment" || lesson["type"] == "Quiz"
|
263
|
+
if contents.nil?
|
264
|
+
repo = ""
|
265
|
+
else
|
266
|
+
if contents[/data-repo=\"(.*?)"/]
|
267
|
+
repo = contents[/data-repo=\"(.*?)"/]
|
268
|
+
repo = repo.slice(11..-2)
|
269
|
+
elsif contents[/class=\"fis-git-link\" href=\"(.*?)"/]
|
270
|
+
repo = contents[/class=\"fis-git-link\" href=\"(.*?)"/]
|
271
|
+
repo = repo.slice(27..-2)
|
272
|
+
else
|
273
|
+
repo = ""
|
274
|
+
end
|
275
|
+
end
|
276
|
+
rescue
|
277
|
+
puts 'Error while mapping course info.'
|
278
|
+
abort
|
279
|
+
end
|
280
|
+
|
281
|
+
if repo != nil && repo != ""
|
282
|
+
if repo.include?('https://github.com/learn-co-curriculum/')
|
283
|
+
lesson["repository"] = repo
|
284
|
+
else
|
285
|
+
lesson["repository"] = "https://github.com/learn-co-curriculum/" + repo
|
286
|
+
puts lesson["repository"] if options[:urls_only]
|
287
|
+
end
|
288
|
+
end
|
289
|
+
sleep(1)
|
290
|
+
lesson
|
291
|
+
end
|
292
|
+
mod
|
293
|
+
end
|
294
|
+
puts course_info.to_yaml if !options[:urls_only]
|
295
|
+
end
|
296
|
+
|
297
|
+
def self.csv(file_to_convert)
|
247
298
|
course_info = YAML.load(File.read("#{Dir.pwd}/#{file_to_convert}"))
|
248
299
|
course_info[:modules] = course_info[:modules].map do |mod|
|
249
300
|
mod[:lessons] = mod[:lessons].map do |lesson|
|
@@ -287,15 +338,41 @@ class CanvasInterface
|
|
287
338
|
end
|
288
339
|
mod
|
289
340
|
end
|
341
|
+
byebug
|
290
342
|
puts course_info.to_yaml
|
291
343
|
end
|
292
344
|
|
293
|
-
def self.
|
294
|
-
|
295
|
-
|
345
|
+
def self.copy_lesson(options)
|
346
|
+
types = ["page", "assignment", "quiz", "discussion"]
|
347
|
+
url = options[:filepath]
|
348
|
+
type = types.find {|type| url.match(type)}
|
349
|
+
options[:type] = type
|
350
|
+
if !url.include?(ENV['CANVAS_API_PATH'])
|
351
|
+
url = url.sub(/^.*\/\/.*?\//,"#{ENV['CANVAS_API_PATH']}/")
|
352
|
+
end
|
296
353
|
|
297
|
-
|
354
|
+
response = RestClient.get(url, headers={
|
355
|
+
"Authorization" => "Bearer #{ENV['CANVAS_API_KEY']}"
|
356
|
+
})
|
298
357
|
|
358
|
+
lesson_info = JSON.parse(response)
|
359
|
+
lesson_info = lesson_info.slice("title",
|
360
|
+
"name",
|
361
|
+
"description",
|
362
|
+
"body",
|
363
|
+
"message",
|
364
|
+
"shuffle_answers",
|
365
|
+
"allowed_attempts",
|
366
|
+
"question_count"
|
367
|
+
)
|
368
|
+
if options[:type] == "page"
|
369
|
+
self.update_existing_lesson(options, lesson_info["title"], lesson_info["body"])
|
370
|
+
else
|
371
|
+
self.update_existing_lesson(options, lesson_info["name"], lesson_info["description"])
|
372
|
+
end
|
373
|
+
|
374
|
+
|
375
|
+
end
|
299
376
|
|
300
377
|
def self.build_payload(options, name, html)
|
301
378
|
if options[:only_update_content]
|
@@ -17,7 +17,7 @@ class RepositoryConverter
|
|
17
17
|
GithubInterface.get_updated_repo(options[:filepath], options[:branch])
|
18
18
|
markdown = RepositoryInterface.read_local_file(options[:filepath], options[:file_to_convert])
|
19
19
|
raw_remote_url = self.set_raw_image_remote_url(options[:filepath])
|
20
|
-
|
20
|
+
markdown = self.escape_existing_html(markdown) if options[:contains_html]
|
21
21
|
markdown = self.fix_local_images(options, markdown, raw_remote_url)
|
22
22
|
html = self.convert_to_html(markdown)
|
23
23
|
# self.fix_local_html_links(options, html, options[:filepath])
|
@@ -26,15 +26,27 @@ class RepositoryConverter
|
|
26
26
|
def self.remote_file_conversion(options)
|
27
27
|
markdown = GithubInterface.read_remote(options[:filepath])
|
28
28
|
raw_remote_url = self.set_raw_image_remote_url(options[:filepath])
|
29
|
+
if options[:contains_html]
|
30
|
+
begin
|
31
|
+
markdown = self.escape_existing_html(markdown)
|
32
|
+
rescue
|
33
|
+
puts "Error reading remote markdown"
|
34
|
+
abort
|
35
|
+
end
|
36
|
+
end
|
37
|
+
if (!options[:branch])
|
38
|
+
options[:branch] = 'master'
|
39
|
+
end
|
29
40
|
markdown = self.fix_local_images(options, markdown, raw_remote_url)
|
30
41
|
html = self.convert_to_html(markdown)
|
31
42
|
# self.fix_local_html_links(options, html, options[:filepath])
|
32
43
|
end
|
33
44
|
|
34
45
|
def self.convert_to_html(markdown)
|
35
|
-
|
46
|
+
renderer = CustomRender.new(escape_html: true, prettify: true, hard_wrap: true)
|
47
|
+
redcarpet = Redcarpet::Markdown.new(CustomRender, options={tables: true, autolink: true, fenced_code_blocks: true, disable_indented_code_blocks: true})
|
36
48
|
html = redcarpet.render(markdown)
|
37
|
-
self.remove_line_breaks(html)
|
49
|
+
# self.remove_line_breaks(html)
|
38
50
|
end
|
39
51
|
|
40
52
|
def self.adjust_converted_html(options, html)
|
@@ -43,12 +55,52 @@ class RepositoryConverter
|
|
43
55
|
html = self.remove_header_and_footer(html)
|
44
56
|
end
|
45
57
|
|
46
|
-
if options[:fis_links]
|
58
|
+
if options[:fis_links] || options[:git_links]
|
47
59
|
html = self.add_fis_links(options, html)
|
48
60
|
end
|
61
|
+
|
62
|
+
if options[:contains_html]
|
63
|
+
html = self.fix_escaped_inline_html_code(html)
|
64
|
+
end
|
65
|
+
|
49
66
|
html
|
50
67
|
end
|
51
68
|
|
69
|
+
def self.fix_escaped_inline_html_code(html)
|
70
|
+
|
71
|
+
# stops HTML/JSX code blocks from rendering as HTML in Canvas
|
72
|
+
html = html.gsub("&gt;</code>", "></code>")
|
73
|
+
html = html.gsub("&gt;</code>", "></code>")
|
74
|
+
|
75
|
+
# fixes < and > code snippets
|
76
|
+
html = html.gsub("&lt;", "<")
|
77
|
+
html = html.gsub("&gt;", ">")
|
78
|
+
|
79
|
+
# # fixes blockquotes
|
80
|
+
# html = html.gsub(/\n<p>>\;(.*)\n>\;/) { |bq|
|
81
|
+
# bq.delete_prefix!("\n<p>>")
|
82
|
+
# "\n<blockquote>" + bq
|
83
|
+
# }
|
84
|
+
# html = html.gsub(/\n>\;(.*)\n>\;/) { |bq|
|
85
|
+
# bq.delete_prefix!("\n>")
|
86
|
+
# " " + bq
|
87
|
+
# }
|
88
|
+
# html = html.gsub(/\n>\;(.*)<\/p>/) { |bq|
|
89
|
+
# bq.delete_prefix!("\n>\;")
|
90
|
+
# bq.delete_suffix!("</p>")
|
91
|
+
# " " + bq + "</blockquote>"
|
92
|
+
# }
|
93
|
+
|
94
|
+
html
|
95
|
+
end
|
96
|
+
|
97
|
+
|
98
|
+
def self.escape_existing_html(markdown)
|
99
|
+
markdown = markdown.gsub(/<\/(?!iframe)/, "</")
|
100
|
+
markdown = markdown.gsub(/<(?!iframe)/, "<")
|
101
|
+
markdown = markdown.gsub(/(?<!iframe)>/, ">")
|
102
|
+
end
|
103
|
+
|
52
104
|
def self.remove_header_and_footer(html)
|
53
105
|
new_html = self.remove_html_header(html)
|
54
106
|
new_html = self.remove_footer(new_html)
|
@@ -56,12 +108,15 @@ class RepositoryConverter
|
|
56
108
|
end
|
57
109
|
|
58
110
|
def self.remove_header(readme)
|
59
|
-
readme.gsub
|
111
|
+
readme = readme.gsub(/^# .+?\n\n/,"")
|
60
112
|
readme.gsub(/^# .+?\n/,"")
|
61
113
|
end
|
62
114
|
|
63
115
|
def self.remove_footer(readme)
|
64
116
|
readme.gsub(/<p class='util--hide'(.+?)<\/p>/,"")
|
117
|
+
readme.gsub(/<p data-visibility='hidden'(.+?)<\/p>/,"")
|
118
|
+
readme.gsub(/<p><\;p data-visibility='\;hidden'(.+?)<\/p>/,"")
|
119
|
+
readme.gsub(/<p><\;p class='util--hide'\;(.+?)<\/p>/,"")
|
65
120
|
end
|
66
121
|
|
67
122
|
def self.remove_html_header(html)
|
@@ -114,7 +169,7 @@ class RepositoryConverter
|
|
114
169
|
end
|
115
170
|
|
116
171
|
def self.adjust_local_markdown_images(readme, raw_remote_url, branch)
|
117
|
-
readme.gsub
|
172
|
+
readme.gsub(/\!\[.+\]\(.+\)/) {|image_markdown|
|
118
173
|
if !image_markdown.match?('amazonaws.com') && !image_markdown.match?('https://') && !image_markdown.match?('http://') && !image_markdown.match?('youtube')
|
119
174
|
image_markdown.gsub!(/\(.+\)/) { |path|
|
120
175
|
path.delete_prefix!("(")
|
@@ -127,12 +182,18 @@ class RepositoryConverter
|
|
127
182
|
end
|
128
183
|
|
129
184
|
def self.adjust_local_html_images(readme, raw_remote_url, branch)
|
130
|
-
readme.gsub
|
131
|
-
|
132
|
-
|
133
|
-
image_source.gsub
|
134
|
-
image_source.
|
135
|
-
|
185
|
+
readme.gsub(/src=(\'|\")[\s\S]*?(\'|\")/) { |image_source|
|
186
|
+
|
187
|
+
if !image_source.match?('amazonaws.com') && !image_source.match?('https://') && !image_source.match?('http://') && !image_source.match?('youtube') && !image_source.match(/src=(\'|\")(?=<%)/)
|
188
|
+
image_source = image_source.gsub(/(\'|\")/, "")
|
189
|
+
image_source = image_source.gsub(/src=/, '')
|
190
|
+
image_source = image_source.strip
|
191
|
+
|
192
|
+
begin
|
193
|
+
'src="' + raw_remote_url + '/' + branch + '/' + image_source + '"'
|
194
|
+
rescue
|
195
|
+
byebug
|
196
|
+
end
|
136
197
|
else
|
137
198
|
image_source
|
138
199
|
end
|
@@ -162,12 +223,12 @@ class RepositoryConverter
|
|
162
223
|
def self.add_fis_links(options, html)
|
163
224
|
repo_info = self.get_repo_info(options[:filepath])
|
164
225
|
html = html.sub(/<div id="git-data-element.*<header class="fis-header.*<\/header>/,'') # remove existing fis header
|
165
|
-
header = self.create_github_link_header(repo_info[:repo_path], options
|
166
|
-
data_element = self.create_data_element(repo_info[:repo_org], repo_info[:repo_name])
|
226
|
+
header = self.create_github_link_header(repo_info[:repo_path], options)
|
227
|
+
data_element = self.create_data_element(repo_info[:repo_org], repo_info[:repo_name], options[:aaq])
|
167
228
|
data_element + header + html
|
168
229
|
end
|
169
230
|
|
170
|
-
def self.create_github_link_header(repo_path,
|
231
|
+
def self.create_github_link_header(repo_path, options)
|
171
232
|
# add link to associated repository
|
172
233
|
github_repo_link = "<a class='fis-git-link' href='#{repo_path}' target='_blank' rel='noopener'><img id='repo-img' title='Open GitHub Repo' alt='GitHub Repo' /></a>"
|
173
234
|
|
@@ -175,16 +236,23 @@ class RepositoryConverter
|
|
175
236
|
github_issue_link = "<a class='fis-git-link' href='#{repo_path}/issues/new' target='_blank' rel='noopener'><img id='issue-img' title='Create New Issue' alt='Create New Issue' /></a>"
|
176
237
|
|
177
238
|
# add link to fork (forking handled by separate Flatiron server, generation of link handled via custom Canvas JS theme file)
|
178
|
-
|
179
|
-
|
239
|
+
|
240
|
+
if (options[:forkable])
|
241
|
+
github_fork_link = "<a class='fis-fork-link' id='fork-link' href='#{repo_path}/fork' target='_blank' rel='noopener'><img id='fork-img' title='Fork This Assignment' alt='Fork This Assignment' /></a>"
|
180
242
|
"<header class='fis-header' style='visibility: hidden;'>#{github_fork_link}#{github_repo_link}#{github_issue_link}</header>"
|
243
|
+
elsif options[:git_links]
|
244
|
+
"<header class='fis-header'>#{github_repo_link}#{github_issue_link}</header>"
|
181
245
|
else
|
182
246
|
"<header class='fis-header' style='visibility: hidden;'>#{github_repo_link}#{github_issue_link}</header>"
|
183
247
|
end
|
184
248
|
end
|
185
249
|
|
186
|
-
def self.create_data_element(repo_org, repo_name)
|
187
|
-
|
250
|
+
def self.create_data_element(repo_org, repo_name, aaq)
|
251
|
+
if (aaq)
|
252
|
+
"<div id='git-data-element' data-aaq='enabled' data-org='#{repo_org}' data-repo='#{repo_name}'></div>"
|
253
|
+
else
|
254
|
+
"<div id='git-data-element' data-org='#{repo_org}' data-repo='#{repo_name}'></div>"
|
255
|
+
end
|
188
256
|
end
|
189
257
|
|
190
258
|
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: github-to-canvas
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.7.pre
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- maxwellbenton
|
@@ -104,11 +104,11 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
104
104
|
version: '0'
|
105
105
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
106
106
|
requirements:
|
107
|
-
- - "
|
107
|
+
- - ">"
|
108
108
|
- !ruby/object:Gem::Version
|
109
|
-
version:
|
109
|
+
version: 1.3.1
|
110
110
|
requirements: []
|
111
|
-
rubygems_version: 3.
|
111
|
+
rubygems_version: 3.2.3
|
112
112
|
signing_key:
|
113
113
|
specification_version: 4
|
114
114
|
summary: github-to-canvas is a tool for migrating and aligning GitHub content with
|