Commit 5f7d3068059e65ca987be48575dae7ed02db8395
1 parent
84a1054c
Exists in
master
and in
1 other branch
- Initial support for dependency graphs.
Showing
4 changed files
with
46 additions
and
5 deletions
Show diff stats
README.md
| ... | ... | @@ -27,7 +27,7 @@ In the end you should be able to run `pip3 --version` and `python3 -c "import sq |
| 27 | 27 | |
| 28 | 28 | Install additional python packages locally on the user area: |
| 29 | 29 | |
| 30 | - pip install --user tornado sqlalchemy pyyaml pygments markdown bcrypt | |
| 30 | + pip install --user tornado sqlalchemy pyyaml pygments markdown bcrypt networkx | |
| 31 | 31 | |
| 32 | 32 | These are usually installed under |
| 33 | 33 | ... | ... |
app.py
| ... | ... | @@ -8,6 +8,8 @@ try: |
| 8 | 8 | import bcrypt |
| 9 | 9 | from sqlalchemy import create_engine |
| 10 | 10 | from sqlalchemy.orm import sessionmaker |
| 11 | + import networkx as nx | |
| 12 | + import yaml | |
| 11 | 13 | except ImportError: |
| 12 | 14 | logger.critical('Python package missing. See README.md for instructions.') |
| 13 | 15 | sys.exit(1) |
| ... | ... | @@ -24,10 +26,17 @@ logger = logging.getLogger(__name__) |
| 24 | 26 | # ============================================================================ |
| 25 | 27 | class LearnApp(object): |
| 26 | 28 | def __init__(self): |
| 29 | + # online students | |
| 27 | 30 | self.online = {} |
| 28 | 31 | |
| 29 | 32 | # connect to database and check registered students |
| 30 | - db = 'students.db' # FIXME | |
| 33 | + self.setup_db('students.db') # FIXME | |
| 34 | + | |
| 35 | + # build dependency graph | |
| 36 | + self.build_dependency_graph({'uevora/lei'}) # FIXME | |
| 37 | + | |
| 38 | + # ------------------------------------------------------------------------ | |
| 39 | + def setup_db(self, db): | |
| 31 | 40 | engine = create_engine(f'sqlite:///{db}', echo=False) |
| 32 | 41 | self.Session = sessionmaker(bind=engine) |
| 33 | 42 | try: |
| ... | ... | @@ -59,7 +68,6 @@ class LearnApp(object): |
| 59 | 68 | 'number': student.id, |
| 60 | 69 | 'knowledge': Knowledge(), # FIXME initial state? |
| 61 | 70 | } |
| 62 | - # print(self.online) | |
| 63 | 71 | return True |
| 64 | 72 | |
| 65 | 73 | # ------------------------------------------------------------------------ |
| ... | ... | @@ -103,3 +111,37 @@ class LearnApp(object): |
| 103 | 111 | raise |
| 104 | 112 | finally: |
| 105 | 113 | session.close() |
| 114 | + | |
| 115 | + # ------------------------------------------------------------------------ | |
| 116 | + # Receives a set of topics (strings like "math/algebra"), | |
| 117 | + # and recursively adds dependencies to the dependency graph | |
| 118 | + def build_dependency_graph(self, topics=set()): | |
| 119 | + g = nx.DiGraph() | |
| 120 | + | |
| 121 | + # add nodes "recursively" following the config.yaml files | |
| 122 | + while topics: | |
| 123 | + t = topics.pop() # take one topic | |
| 124 | + if t in g.nodes(): # skip it if already in the graph | |
| 125 | + continue | |
| 126 | + | |
| 127 | + # get configuration dictionary for this topic | |
| 128 | + # dictionary has keys: type, title, depends | |
| 129 | + try: | |
| 130 | + with open(f'topics/{t}/config.yaml', 'r') as f: | |
| 131 | + logger.info(f'Loading {t}') | |
| 132 | + config = yaml.load(f) | |
| 133 | + except FileNotFoundError: | |
| 134 | + logger.error(f'Not found: topics/{t}/config.yaml') | |
| 135 | + continue | |
| 136 | + | |
| 137 | + config.setdefault('depends', set()) # make sure 'depends' key exists | |
| 138 | + topics.update(config['depends']) | |
| 139 | + g.add_node(t, config=config) | |
| 140 | + | |
| 141 | + # add edges topic -> dependency | |
| 142 | + for t in g.nodes(): | |
| 143 | + deps = g.node[t]['config']['depends'] | |
| 144 | + g.add_edges_from([(t, d) for d in deps]) | |
| 145 | + | |
| 146 | + self.depgraph = g | |
| 147 | + logger.info(f'Graph has {g.number_of_nodes()} nodes and {g.number_of_edges()} edges') | ... | ... |
config/logger.yaml
serve.py
| ... | ... | @@ -82,7 +82,6 @@ class LoginHandler(BaseHandler): |
| 82 | 82 | def post(self): |
| 83 | 83 | uid = self.get_body_argument('uid') |
| 84 | 84 | pw = self.get_body_argument('pw') |
| 85 | - # print(f'login.post: user={uid}, pw={pw}') | |
| 86 | 85 | |
| 87 | 86 | if self.learn.login(uid, pw): |
| 88 | 87 | logging.info(f'User "{uid}" login ok.') | ... | ... |