Unnamed sections not showing bug fixed, heuristic folder naming

This commit is contained in:
Mattia Mascarello 2023-05-01 20:20:12 +02:00
parent 7fb6ea83b2
commit 0ee02e4a7e
3 changed files with 150 additions and 48 deletions

187
FSNode.py
View File

@ -7,16 +7,74 @@ from markdownify import markdownify as md
from moodle import Category, Course, Moodle, Section, Folder, File, Label, Url from moodle import Category, Course, Moodle, Section, Folder, File, Label, Url
import unicodedata
import re
def slugify(value, allow_unicode=False):
"""
Taken from https://github.com/django/django/blob/master/django/utils/text.py
Convert to ASCII if 'allow_unicode' is False. Convert spaces or repeated
dashes to single dashes. Remove characters that aren't alphanumerics,
underscores, or hyphens. Convert to lowercase. Also strip leading and
trailing whitespace, dashes, and underscores.
"""
value = str(value)
s = value.rsplit(".", 1) # split into name and extension
ext = ""
if len(s) == 2:
value = s[0]
ext = "." + slugify(s[1])
if allow_unicode:
value = unicodedata.normalize('NFKC', value)
else:
value = unicodedata.normalize('NFKD', value).encode('ascii', 'ignore').decode('ascii')
value = re.sub(r'[^\w\s-]', '', value.lower())
res = re.sub(r'[-\s]+', '-', value).strip('-_')
file = res + ext
return file
class LinkTo:
pass
class UrlLink(LinkTo):
def __init__(self, url: str):
self.url = url
class FileLink(LinkTo):
def __init__(self, url: str):
self.url = url
class HTMLContentMap(LinkTo):
def __init__(self):
self.htmlMap = {}
def add(self, html: str, label_id: int):
self.htmlMap[label_id] = md(html)
def sort(self):
self.htmlMap = dict(sorted(self.htmlMap.items()))
def markdown_make(self) -> str:
return "\n".join([self.htmlMap[key] for key in self.htmlMap])
fileCache = {} fileCache = {}
# Virtual Filesystem node # Virtual Filesystem node
class FSNode: class FSNode:
def __init__(self, name: str, is_dir=True): def __init__(self, name: str, parent, is_dir=True):
self.name = name self.name = slugify(name)
self.parent = parent
self.children = [] self.children = []
self.is_dir = is_dir self.is_dir = is_dir
self.size = 0 self.size = 0
self.url = None self.linkTo = None
def to_stat_struct(self): def to_stat_struct(self):
if self.is_dir: if self.is_dir:
@ -38,6 +96,12 @@ class FSNode:
st_size=self.size st_size=self.size
) )
def find_child_by_name(self, name: str):
for el in self.children:
if el.name == name:
return el
return None
def resolve_path(self, path: str): def resolve_path(self, path: str):
if path == "/": if path == "/":
return self return self
@ -53,55 +117,83 @@ class FSNode:
return None return None
@staticmethod @staticmethod
def from_category(c: Category, m: Moodle): def from_category(c: Category, parent, m: Moodle):
f = FSNode(c.name, True) f = FSNode(c.name, parent, True)
for course in c.courses: for course in c.courses:
f.children.append(FSNode.from_course(course, m)) f.children.append(FSNode.from_course(course, f, m))
return f return f
@staticmethod @staticmethod
def from_course(c: Course, m: Moodle): def from_course(c: Course, parent, m: Moodle):
f = FSNode(c.shortname, True) f = FSNode(c.shortname, parent, True)
for s in m.get_course_content(c.id): for s in m.get_course_content(c.id):
f.children.append(FSNode.from_section(s, m)) f.children.append(FSNode.from_section(s, f, m))
return f return f
@staticmethod @staticmethod
def from_section(s: Section, m: Moodle): def from_section(s: Section, parent, m: Moodle):
f = FSNode(s.name, True) f = FSNode(s.name, parent, True)
for mo in s.modules:
f.children.append(FSNode.from_module(mo, m))
if len(s.htmlcontent): if len(s.htmlcontent):
m = FSNode("README.md", False) m = FSNode("README.md", f, False)
m.size = len(s.htmlcontent) m.linkTo = HTMLContentMap()
m.url = "html#" + s.htmlcontent m.linkTo.add(s.htmlcontent, s.id)
m.size = len(m.linkTo.markdown_make())
f.children.append(m) f.children.append(m)
for mo in s.modules:
el = FSNode.from_module(mo, f, m)
if el is not None:
f.children.append(el)
if s.autoName: # Heuristic name generation
q = f.find_child_by_name(slugify("README.md"))
if q is not None:
md = q.linkTo.markdown_make()
lines = md.split("\n")
line = lines[0]
i = 1
while not len(re.sub(r'\W+', '', line)) and i < len(lines):
line = lines[i]
i += 1
if i != len(lines):
title = slugify(line)
# truncate title to 30 chars
title = title[:30]
f.name = title
return f return f
@staticmethod @staticmethod
def from_module(mo, m: Moodle): def from_module(mo, parent, m: Moodle):
if isinstance(mo, Folder): if isinstance(mo, Folder):
f = FSNode(mo.name, True) f = FSNode(mo.name, parent, True)
for el in mo.files: for el in mo.files:
r = FSNode.from_module(el, m) r = FSNode.from_module(el, f, m)
if r is not None: if r is not None:
f.children.append(r) f.children.append(r)
return f return f
elif isinstance(mo, File): elif isinstance(mo, File):
return FSNode.from_file(mo, m) return FSNode.from_file(mo, parent, m)
elif isinstance(mo, Label): elif isinstance(mo, Label):
return FSNode.from_label(mo, m) FSNode.from_label(mo, parent, m)
elif isinstance(mo, Url): elif isinstance(mo, Url):
return FSNode.from_url(mo, m) return FSNode.from_url(mo, parent, m)
return None return None
@staticmethod @staticmethod
def from_file(el: File, m: Moodle): def from_file(el: File, parent, m: Moodle):
f = FSNode(el.name, False) f = FSNode(el.name, parent, False)
f.size = el.filesize f.size = el.filesize
f.url = el.fileurl f.linkTo = FileLink(el.fileurl)
return f return f
@staticmethod
def hash_link_to(a: LinkTo):
if isinstance(a, UrlLink):
return "url#" + a.url
elif isinstance(a, FileLink):
return a.url
elif isinstance(a, HTMLContentMap):
return "html#" + str(a.htmlMap.keys())
return None
def read(self, size, offset, mo: Moodle): def read(self, size, offset, mo: Moodle):
global fileCache global fileCache
if sys.getsizeof(fileCache) > 3 * 100000000 and len(fileCache) > 2: if sys.getsizeof(fileCache) > 3 * 100000000 and len(fileCache) > 2:
@ -109,33 +201,40 @@ class FSNode:
for k in list(d.keys())[:len(d) // 2]: for k in list(d.keys())[:len(d) // 2]:
del fileCache[k] del fileCache[k]
del d del d
if self.url not in fileCache.keys(): link_hash = FSNode.hash_link_to(self.linkTo)
if self.url.startswith("html#"): if link_hash not in fileCache.keys():
fileCache[self.url] = {"datetime": time.time(), "content": md(self.url[5:]).encode()} if isinstance(self.linkTo, HTMLContentMap):
elif self.url.startswith("url#"): fileCache[self.linkTo] = {"datetime": time.time(), "content": self.linkTo.markdown_make().encode()}
fileCache[self.url] = {"datetime": time.time(), "content": self.url[4:].encode()} elif isinstance(self.linkTo, UrlLink):
else: fileCache[self.linkTo] = {"datetime": time.time(), "content": self.linkTo.url.encode()}
r = requests.get(self.url + "&token=" + mo.token) elif isinstance(self.linkTo, FileLink):
fileCache[self.url] = {"datetime": time.time(), "content": r.content} r = requests.get(self.linkTo.url + "&token=" + mo.token)
return fileCache[self.url]["content"][offset:offset + size] fileCache[self.linkTo] = {"datetime": time.time(), "content": r.content}
return fileCache[self.linkTo]["content"][offset:offset + size]
@staticmethod @staticmethod
def from_moodle(moodle: Moodle): def from_moodle(moodle: Moodle):
f = FSNode("/", True) f = FSNode("/", None, True)
for cat in moodle.get_enrolled_categories(): for cat in moodle.get_enrolled_categories():
f.children.append(FSNode.from_category(cat, moodle)) f.children.append(FSNode.from_category(cat, f, moodle))
return f return f
@staticmethod @staticmethod
def from_label(mo: Label, m): def from_label(mo: Label, parent, m) -> None:
f = FSNode(mo.name, False) f = parent.find_child_by_name(slugify("README.md"))
f.size = len(mo.htmlcontent) if f is not None:
f.url = "html#" + mo.htmlcontent f.linkTo.add(mo.htmlcontent, mo.id)
return f else:
f = FSNode(slugify("README.md"), parent, False)
f.linkTo = HTMLContentMap()
f.linkTo.add(mo.htmlcontent, mo.id)
f.size = len(f.linkTo.markdown_make())
parent.children.append(f)
return None
@staticmethod @staticmethod
def from_url(mo: Url, m): def from_url(mo: Url, parent, m):
f = FSNode(mo.name, False) f = FSNode(mo.name, parent, False)
f.size = len(mo.url) f.size = len(mo.url)
f.url = "url#" + mo.url f.linkTo = UrlLink(mo.url)
return f return f

View File

@ -24,8 +24,6 @@ MOUNT = os.getenv('MOUNT')
m = Moodle(SITE, MOODLE_USERNAME, PASSWORD) m = Moodle(SITE, MOODLE_USERNAME, PASSWORD)
m.login() m.login()
print(m.get_user().__dict__) #logging.basicConfig(level=logging.DEBUG)
logging.basicConfig(level=logging.DEBUG)
if __name__ == '__main__': if __name__ == '__main__':
FUSE(MoodleFS(m), MOUNT, foreground=True, allow_other=True) FUSE(MoodleFS(m), MOUNT, foreground=True, allow_other=True)

View File

@ -1,3 +1,5 @@
import re
import requests import requests
# a minimalistic moodle api wrapper, just for the purpose of this project # a minimalistic moodle api wrapper, just for the purpose of this project
@ -25,8 +27,10 @@ class Course:
class Section: class Section:
def __init__(self, id_i: int, name: str, htmlcontent: str, modules: list): def __init__(self, id_i: int, name: str, htmlcontent: str, modules: list):
self.id = id_i self.id = id_i
if len(name.strip()) == 0: self.autoName = False
if len(re.sub(r'\W+', '', name)) == 0:
name = "Section " + str(id_i) name = "Section " + str(id_i)
self.autoName = True
self.name = name self.name = name
self.htmlcontent = htmlcontent self.htmlcontent = htmlcontent
self.modules = modules self.modules = modules
@ -86,6 +90,7 @@ class Moodle:
raise Exception(r["error"]) raise Exception(r["error"])
self.token = r["token"] self.token = r["token"]
self.private_token = r["privatetoken"] self.private_token = r["privatetoken"]
self.get_user()
def get_user(self) -> MoodleUser: def get_user(self) -> MoodleUser:
url = self.site_url + "/webservice/rest/server.php?moodlewsrestformat=json" url = self.site_url + "/webservice/rest/server.php?moodlewsrestformat=json"