Skip to content

Commit

Permalink
Merge branch 'feature/summer2024' of https://github.com/ofloveandhate…
Browse files Browse the repository at this point in the history
…/markdown2canvas into feature/summer2024
  • Loading branch information
ofloveandhate committed Jul 19, 2024
2 parents d4967ae + 357dbd6 commit 1d1e658
Show file tree
Hide file tree
Showing 17 changed files with 312 additions and 30 deletions.
1 change: 1 addition & 0 deletions docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ Tutorials
tutorials/writing_content
tutorials/styling_content
tutorials/text_replacements
tutorials/uploading_files
tutorials/publishing_content


Expand Down
2 changes: 2 additions & 0 deletions docs/tutorials/text_replacements.rst
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ Note that `_course_metadata/replacements.json` is just a regular old JSON file.
Usage
--------

Keep in mind that the order of replacement is unspecified. Thus, it is important to choose keys that will not appear within values, and will not appear within source documents where replacement is undesired.


Custom text replacements per-content
--------------------------------------
Expand Down
42 changes: 42 additions & 0 deletions docs/tutorials/uploading_files.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
How to upload a file
--------------------------------------------------------------------------



The `meta.json` file
====================

Filenames and titles of files are distinct on canvas:
the latter is what you will see when the file is placed in a module, while the former is what is shown in the file structure.

You can place a file in as many modules as you wish by specifying the modules in the `meta.json` file.
The key `module` has a value which is a list of names of modules in the Canvas course.
If no module with the specified name exists, a module will be created to house the file.

The `destination` key specifies where in the file structure you would like the file to be placed.

Note that while a file cannot be simultaneously placed in multiple file structure locations using `meta.json`, if `meta.json` is updated,
the file will not automatically be deleted from any previous location unless that instance is specifically deleted.


Example
=======

If the `meta.json` file looks like:

.. code-block::
{
"type":"file",
"title":"Syllabus",
"filename":"F24_Math100_syllabus.pdf",
"modules":["Course Information", "Week 1"],
"destination": "course_info/syllabus_schedule"
}
then the file `F24_Math100_syllabus.pdf` will be put into two modules: `Course Information` and `Week 1`.
Within these two modules, its title will appear to students as `Syllabus`. The file will be located in `course_info/syllabus_schedule`,
which will be created if it did not already exist.



Binary file added test/_styles/custom/Ruapehu_and_Ngauruhoe.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 4 additions & 0 deletions test/_styles/custom/footer.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@


</div>

4 changes: 4 additions & 0 deletions test/_styles/custom/footer.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
---

Header image credit: Jeremy Visser, CC BY-SA 4.0 <https://creativecommons.org/licenses/by-sa/4.0>, via Wikimedia Commons.

7 changes: 7 additions & 0 deletions test/_styles/custom/header.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<link href="https://media.uwex.edu/app/droplets_v3/css/droplets.css" rel="stylesheet"></link>
<script href="https://media.uwex.edu/app/droplets_v3/script/droplets.js" type="test/javascript"></script>


<div id="uws-droplets-page">
<!-- Droplets elements and components -->

4 changes: 4 additions & 0 deletions test/_styles/custom/header.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@

![This is a photo of Mount Ruapehu and Mount Ngauruhoe looking west from the Desert Road in Tongariro National Park (New Zealand) in January 2015.]($PATHTOMD2CANVASSTYLEFILE/Ruapehu_and_Ngauruhoe.jpg)

---
2 changes: 1 addition & 1 deletion test/a_file.file/meta.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,6 @@
"type":"file",
"title":"automatically uploaded file: ds150_course_logo.pdf",
"filename":"ds150_course_logo.pdf",
"modules":["Automatically Added Test Module", "Another automatically added test module"],
"modules":["Automatically Added Test Module", "Another automatically added test module", "module created by file upload"],
"destination": "automatically_uploaded_files/a_subfolder"
}
44 changes: 43 additions & 1 deletion test/test_file.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
import markdown2canvas as mc

import pytest
import json
import datetime

@pytest.fixture(scope='class')
def course():
Expand All @@ -16,12 +18,28 @@ def course():
yield canvas.get_course(test_course_id)

@pytest.fixture(scope='class')
def content(course):
def content():
import os
folder = 'a_file.file'

yield mc.File(folder)

#the following gives all instances of that file, if it lives in multiple locations
@pytest.fixture(scope='class')
def file_list(course):
yield course.get_files(search_term = 'ds150_course_logo.pdf')

#gives the current instance, based on the destination in meta.json
@pytest.fixture(scope='class')
def current_file(course, file_list):
with open('a_file.file/meta.json', "r", encoding="utf-8") as f:
folder_name = json.loads(f.read())['destination']
for instance in file_list:
if instance.folder_id == course.get_folders(search_term=folder_name)[0].id:
yield instance





class TestFile():
Expand All @@ -33,9 +51,24 @@ def test_meta(self, content):
def test_can_publish(self, course, content):
content.publish(course,overwrite=True)
assert content.is_already_uploaded(course)

def test_in_modules(self, course, content):
content.publish(course,overwrite=True)
for m in content.metadata['modules']:
assert content.is_in_module(course, m)
module_test = course.get_modules(search_term = m)[0]
assert module_test.get_module_items(search_term = 'ds150')[0].title == 'automatically uploaded file: ds150_course_logo.pdf'

#tests that it ends up in the folder you specified this time (it can simultaneously be in another folder if you put it there previously)
def test_in_folder(self, course, content, file_list, current_file):
content.publish(course,overwrite=True)
with open('a_file.file/meta.json', "r", encoding="utf-8") as f:
folder_name = json.loads(f.read())['destination']
folder_list=[]
for instance in file_list:
folder_list.append(instance.folder_id)
assert course.get_folders(search_term=folder_name)[0].id in folder_list
assert current_file.folder_id == course.get_folders(search_term=folder_name)[0].id

def test_already_online_raises(self, course, content):
# publish once, forcefully.
Expand All @@ -46,5 +79,14 @@ def test_already_online_raises(self, course, content):
content.publish(course,overwrite=False) # default is False


def test_attributes(self, course, content, current_file):
content.publish(course,overwrite=True)
assert current_file.filename == 'ds150_course_logo.pdf'
assert current_file.modified_at_date.day == datetime.date.today().day







7 changes: 5 additions & 2 deletions test/test_page.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,7 @@ def test_already_online_raises(self, course, page_has_local_images):

def test_doesnt_find_deleted(self, course, page_has_local_images):
name = page_has_local_images.name

page_has_local_images.publish(course,overwrite=True)
assert mc.is_page_already_uploaded(name,course)
f = mc.find_page_in_course(name,course)
f.delete()
assert not mc.is_page_already_uploaded(name,course)
Expand All @@ -54,3 +52,8 @@ def test_can_find_published(self, course, page_has_local_images):
page_has_local_images.publish(course,overwrite=True)
assert mc.is_page_already_uploaded(page_has_local_images.name,course)

def test_content(self, course):
content = course.get_pages(search_term='Test Has Local Images')[0].show_latest_revision().body
assert 'testing source including images' in content
assert 'alt="A menagerie of Herwig Hauser surfaces"' in content

12 changes: 5 additions & 7 deletions test/test_page_in_module.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,9 @@ def destination_modules(page_plain_text_in_a_module):
yield page.metadata['modules']


#self._delete_test_modules()

def _delete_test_modules(self):
for m in self.destination_modules:
mc.delete_module(m, self.course, even_if_exists=True)
def _delete_test_modules(course, destination_modules):
for m in destination_modules:
mc.delete_module(m, course, even_if_doesnt_exist=True)



Expand Down Expand Up @@ -64,12 +62,12 @@ def test_can_make_modules(self, course, destination_modules):


def test_can_delete_modules(self, course, destination_modules):

_delete_test_modules(course, destination_modules)
for m in destination_modules:
mc.create_or_get_module(m,course)

for m in destination_modules:
mc.delete_module(m, course, even_if_exists=False)
mc.delete_module(m, course, even_if_doesnt_exist=False)



Expand Down
104 changes: 101 additions & 3 deletions test/test_replacements.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,11 @@
sys.path.insert(0,'../')
import markdown2canvas as mc

import json

import pytest


@pytest.fixture(scope='class')
def course():
import os
Expand All @@ -17,29 +20,124 @@ def course():


@pytest.fixture(scope='class')
def page_using_defaults(course):
def page_using_defaults():
import os
folder = 'uses_replacements_default'

yield mc.Page(folder)



@pytest.fixture(scope='class')
def page_using_custom(course):
def page_using_custom():
import os
folder = 'uses_replacements_custom'

yield mc.Page(folder)


@pytest.fixture(scope='class')
def default_filename():
with open('_course_metadata/defaults.json', "r", encoding="utf-8") as f:
defaults = f.read()
yield json.loads(defaults)['replacements']

@pytest.fixture(scope='class')
def replacements_default(default_filename):
with open(default_filename, "r", encoding="utf-8") as f:
yield f.read()

@pytest.fixture(scope='class')
def uses_defaults_source():
with open('uses_replacements_default/source.md', "r", encoding="utf-8") as f:
yield f.read()


@pytest.fixture(scope='class')
def html_using_defaults(course):
a = course.get_pages(search_term = 'Test replacements using default replacements file')[0]
rev = a.show_latest_revision()
yield rev.body


@pytest.fixture(scope='class')
def replacements_custom():
with open('_course_metadata/replacements2.json', "r", encoding="utf-8") as f:
yield f.read()

@pytest.fixture(scope='class')
def uses_custom_source():
with open('uses_replacements_custom/source.md', "r", encoding="utf-8") as f:
yield f.read()

@pytest.fixture(scope='class')
def html_using_custom(course):
a = course.get_pages(search_term = 'Test replacements with custom replacements file')[0]
rev = a.show_latest_revision()
yield rev.body





class TestPage():

def test_can_publish(self, course, page_using_defaults, page_using_custom):
page_using_defaults.publish(course,overwrite=True)
page_using_custom.publish(course,overwrite=True)


##Removed a " as e_info" after the def in the following... doesn't seem to have hurt it?
def test_get_default_replacements_name(self):
path = mc.get_default_replacements_name()
assert path == '_course_metadata/replacements.json'


def test_removed_default(self, html_using_defaults, replacements_default, uses_defaults_source):
replacements_dict_default = json.loads(replacements_default)
for key in replacements_dict_default:
if key in uses_defaults_source:
assert key not in html_using_defaults
#Want to add something about the new thing being in the html
#assert replacements_dict_default[key] in html_using_defaults

def test_replaced_default(self, html_using_defaults):
#default replacements that should translate seamlessly
assert 'with this text' in html_using_defaults
assert 'destination_without_spaces' in html_using_defaults
#check specific video options
assert '560' in html_using_defaults
assert '315' in html_using_defaults
assert 'https://www.youtube.com/embed/dQw4w9WgXcQ?si=BqTm4nbZOLTHaxnz' in html_using_defaults
assert 'YouTube video player' in html_using_defaults
assert 'accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share' in html_using_defaults
assert 'allowfullscreen' in html_using_defaults

def test_removed_custom(self, html_using_custom, uses_custom_source, replacements_custom):
replacements_dict_custom = json.loads(replacements_custom)
for key in replacements_dict_custom:
if key in uses_custom_source:
assert key not in html_using_custom
assert replacements_dict_custom[key] in html_using_custom

def test_replaced_custom(self, html_using_custom):
#custom replacements that should translate seamlessly
assert 'target custom replacement without space' in html_using_custom
assert 'target custom replacement from nospace' in html_using_custom

def test_incorrect_replacement_custom(self, html_using_custom):
#First check that none of the default replacements show up in the custom replacements file
assert 'with this text' not in html_using_custom
assert 'destination_without_spaces' not in html_using_custom
assert 'https://www.youtube.com/embed/dQw4w9WgXcQ?si=BqTm4nbZOLTHaxnz' not in html_using_custom


def test_incorrect_replacement_default(self, html_using_defaults):
#First check that none of the default replacements show up in the custom replacements file
assert 'target custom replacement without space' not in html_using_defaults
assert 'target custom replacement from nospace' not in html_using_defaults



def test_missing_replacements(self):
# constructing a page with a replacements file that doesn't exist should raise
with pytest.raises(FileNotFoundError):
Expand Down
Loading

0 comments on commit 1d1e658

Please sign in to comment.