Skip to content

Commit

Permalink
Generalize "course" to "context"
Browse files Browse the repository at this point in the history
Users and groups can now be mounted too.
  • Loading branch information
shreyasminocha committed Feb 11, 2021
1 parent b907b8a commit 8e59a3b
Show file tree
Hide file tree
Showing 5 changed files with 50 additions and 33 deletions.
18 changes: 12 additions & 6 deletions canvasfs/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import trio

from .canvas_fs import CanvasFs
from .utilities import Context

try: import faulthandler
except ImportError: pass
Expand All @@ -33,18 +34,23 @@ def init_logging(debug=False):
root_logger.addHandler(handler)

def parse_args():
parser = ArgumentParser()
parser = ArgumentParser(allow_abbrev=False)

parser.add_argument('context_id', type=int, help='the ID of the context to be mounted')
parser.add_argument('mountpoint', type=str, help='path to mount the FS at')

parser.add_argument('-c', '--context', choices=['course', 'user', 'group'], default='course', help='canvas context type')
parser.add_argument('--debug', action='store_true', default=False, help='enable debugging output')
parser.add_argument('--debug-fuse', action='store_true', default=False, help='enable FUSE debugging output')

parser.add_argument('course_id', type=int, help='The ID of the course to be mounted')
parser.add_argument('mountpoint', type=str, help='Where to mount the file system')
parser.add_argument('--debug', action='store_true', default=False, help='Enable debugging output')
parser.add_argument('--debug-fuse', action='store_true', default=False, help='Enable FUSE debugging output')
return parser.parse_args()

options = parse_args()
init_logging(options.debug)

canvas_fs = CanvasFs(options.course_id)
context = Context(options.context + 's')

canvas_fs = CanvasFs(options.context_id, context)
fuse_options = set(pyfuse3.default_options)
fuse_options.add('fsname=canvas')

Expand Down
22 changes: 10 additions & 12 deletions canvasfs/canvas_files.py
Original file line number Diff line number Diff line change
@@ -1,50 +1,48 @@
import errno
import os
import enum

import requests
from cachecontrol import CacheControl
from dotenv import load_dotenv

from .utilities import Context, Item

load_dotenv()

CANVAS_URL = os.getenv('CANVAS_URL')
ACCESS_TOKEN = os.getenv('ACCESS_TOKEN')
API_URL = f'{CANVAS_URL}/api/v1'

class Item(enum.Enum):
FOLDER = 1
FILE = 2

class CanvasCourseFiles():
def __init__(self, course_id):
class CanvasFiles():
def __init__(self, context_id, context=Context.COURSE):
api = requests.Session()
api.headers['Authorization'] = f'Bearer {ACCESS_TOKEN}'

self.api = CacheControl(api)
self.course_id = course_id
self.context = context
self.context_id = context_id

def get_folder(self, folder_id):
url = f'{API_URL}/courses/{self.course_id}/folders/{folder_id}'
url = f'{API_URL}/{self.context}/{self.context_id}/folders/{folder_id}'
response = self.api.get(url)

if response.status_code == requests.codes.unauthorized:
raise ConnectionError()

if response.status_code == requests.codes.not_found:
raise FileNotFoundError(errno.ENOENT, os.strerror(errno.ENOENT), '$filename')
raise FileNotFoundError(errno.ENOENT, os.strerror(errno.ENOENT))

return response.json()

def get_file(self, file_id):
url = f'{API_URL}/courses/{self.course_id}/files/{file_id}'
url = f'{API_URL}/{self.context}/{self.context_id}/files/{file_id}'
response = self.api.get(url)

if response.status_code == requests.codes.unauthorized:
raise ConnectionError()

if response.status_code == requests.codes.not_found:
raise FileNotFoundError(errno.ENOENT, os.strerror(errno.ENOENT), '$filename')
raise FileNotFoundError(errno.ENOENT, os.strerror(errno.ENOENT))

return response.json()

Expand Down
26 changes: 13 additions & 13 deletions canvasfs/canvas_fs.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,15 @@

import pyfuse3

from .canvas_files import CanvasCourseFiles, Item
from .utilities import iso_to_unix
from .canvas_files import CanvasFiles
from .utilities import Context, Item, iso_to_unix

class CanvasFs(pyfuse3.Operations):
supports_dot_lookup = False

def __init__(self, course_id):
def __init__(self, context_id, context=Context.COURSE):
super(CanvasFs, self).__init__()
self.course = CanvasCourseFiles(course_id)
self.context = CanvasFiles(context_id, context)

async def getattr(self, inode, ctx=None, **kwargs):
entry = pyfuse3.EntryAttributes()
Expand All @@ -26,8 +26,8 @@ async def getattr(self, inode, ctx=None, **kwargs):
entry.st_ctime_ns = int(1 * 1e9)

else:
item = kwargs.get('item') or self.course.get_item(inode)
item_type = CanvasCourseFiles.item_type(item)
item = kwargs.get('item') or self.context.get_item(inode)
item_type = CanvasFiles.item_type(item)

if item_type == Item.FOLDER:
entry.st_mode = (stat.S_IFDIR | 0o755)
Expand All @@ -54,7 +54,7 @@ async def lookup(self, parent_inode, name, ctx=None):
if parent_inode == pyfuse3.ROOT_INODE:
parent_inode = 'root'

parent_folder_files = self.course._ls_files(parent_inode)
parent_folder_files = self.context._ls_files(parent_inode)

found_file = None
for file in parent_folder_files:
Expand All @@ -72,8 +72,8 @@ async def opendir(self, inode, ctx):
return inode

try:
item = self.course.get_item(inode)
item_type = CanvasCourseFiles.item_type(item)
item = self.context.get_item(inode)
item_type = CanvasFiles.item_type(item)
except FileNotFoundError:
raise pyfuse3.FUSEError(errno.ENOENT)

Expand All @@ -86,7 +86,7 @@ async def readdir(self, fh, start_id, token):
if fh == pyfuse3.ROOT_INODE:
fh = 'root'

ls = self.course.ls(fh)
ls = self.context.ls(fh)

for i in range(start_id, len(ls)):
item = ls[i]
Expand All @@ -101,7 +101,7 @@ async def readdir(self, fh, start_id, token):

async def open(self, inode, flags, ctx):
try:
file = self.course.get_file(inode)
file = self.context.get_file(inode)
except:
raise pyfuse3.FUSEError(errno.ENOENT)

Expand All @@ -112,12 +112,12 @@ async def open(self, inode, flags, ctx):

async def read(self, fh, offset, size):
try:
file = self.course.get_file(fh)
file = self.context.get_file(fh)
except:
raise pyfuse3.FUSEError(errno.ENOENT)

file_url = file['url']
downloaded_chunk = await self.course.download_file(file_url, offset, size)
downloaded_chunk = await self.context.download_file(file_url, offset, size)

return downloaded_chunk

Expand Down
13 changes: 13 additions & 0 deletions canvasfs/utilities.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,19 @@
import enum
from datetime import datetime
import calendar

class Item(enum.Enum):
FOLDER = 1
FILE = 2

class Context(enum.Enum):
COURSE = 'courses'
USER = 'users'
GROUP = 'groups'

def __str__(self):
return self.value

def iso_to_unix(iso):
iso_format = '%Y-%m-%dT%H:%M:%SZ'

Expand Down
4 changes: 2 additions & 2 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ This is, at the moment, just a proof-of-concept.
- [x] read
- [ ] write
- [x] mount courses
- [ ] mount groups
- [ ] mount users
- [x] mount groups
- [x] mount users

## Getting started

Expand Down

0 comments on commit 8e59a3b

Please sign in to comment.