forked from ISI-TechKnAcq/techknacq-tk
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathserver
More file actions
executable file
·185 lines (156 loc) · 5.64 KB
/
Copy pathserver
File metadata and controls
executable file
·185 lines (156 loc) · 5.64 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
#!/usr/bin/env python3
# TechKnAcq: Reading list server
# Jonathan Gordon
import sys
import os
import time
import ssl
import click
from collections import defaultdict
from flask import Flask, request, jsonify
from flask_cors import CORS
from techknacq.conceptgraph import ConceptGraph
from techknacq.readinglist import ReadingList
app = Flask(__name__)
CORS(app)
cg = ConceptGraph()
# This has fewer limitations than `timeit`.
def timed(f):
start = time.time()
ret = f()
elapsed = time.time() - start
return ret, elapsed
@app.route('/readingList', methods=['GET'])
def handle_request():
try:
q = request.args.get('query')
except:
return {'Error': 'Bad request.'}
# I ignore the 'd' parameter specifying the number of docs to return.
# I use the values for 't' to decide the user model.
try:
level = int(request.args.get('t'))
except:
level = 4 # Intermediate
print('Generating reading list for', q + ':', end=' ')
user_model = {}
for c in cg.concepts():
user_model[c] = level
r, elapsed = timed(lambda: ReadingList(cg, q.strip().split(), user_model))
print('%.4f seconds.' % (elapsed))
def topic_entry(topic):
nonlocal doc_index, resp
node = {'id': topic['id'].replace('concept-', ''),
'label': topic['name'],
'matched': False}
resp['graphResponse']['nodes'].append(node)
entry = {'index': topic['id'].replace('concept-', ''),
'topicName': topic['name'],
'dependentTopics': [],
'documents': []}
for doc in topic['documents1']:
entry['documents'].append(doc_entry(doc))
doc_index += 1
for subtopic in topic['subconcepts']:
entry['dependentTopics'].append(topic_entry(subtopic))
for doc in topic['documents2']:
entry['documents'].append(doc_entry(doc))
doc_index += 1
return entry
def doc_entry(doc):
nonlocal doc_index, r
entry = {'index': doc_index,
'id': doc['id'],
'author': '; '.join(doc['authors']),
'authorScore': 0.0,
'title': doc['title'],
'year': doc['year'],
'relevanceScore': 0.0,
'readabilityScore': 0.0,
'pageRankScore': 0.0,
'pedagogicalRole': None,
'relevantTopics': [],
'url': doc['url'],
'abstractText': ' '.join(doc['abstract'])}
for topic in r.all_concepts():
try:
topic_json = {
'topicName': topic['name'],
'strength': cg.doc_topic_strength(doc['id'], topic['id'])
}
entry['relevantTopics'].append(topic_json)
except:
continue
return entry
resp = {'keyword': q,
'baseLineDocuments': [],
'graphResponse': {
'edges': [],
'nodes': []},
'matchTopics': []}
doc_index = 0
for topic in r.rl:
resp['matchTopics'].append(topic_entry(topic))
# Add dependency edges to the graph in the response for all topic nodes
# we added.
topics = set(['concept-' + x['id'] for x in
resp['graphResponse']['nodes']])
edges = defaultdict(dict)
for topic in topics:
for dep_id, dep_weight in cg.topic_deps(topic):
if dep_id in topics:
node_from = topic.replace('concept-', '')
node_to = dep_id.replace('concept-', '')
edges[node_from][node_to] = dep_weight
# Make graph edges unidirectional.
new_edges = defaultdict(dict)
for node_from in edges:
for node_to in edges[node_from]:
if node_to in edges and node_from in edges[node_to] and \
edges[node_to][node_from] > edges[node_from][node_to]:
new_edges[node_to][node_from] = edges[node_to][node_from]
else:
new_edges[node_from][node_to] = edges[node_from][node_to]
edges = dict(new_edges)
# Perform transitive reduction.
remove = []
for e1_n1 in edges:
for e1_n2 in edges[e1_n1]:
for e2_n2 in edges[e1_n1]:
if e2_n2 == e1_n2:
continue
if e1_n2 in edges.get(e2_n2, []):
remove.append((e1_n1, e1_n2))
# Don't disconnect a node.
for n1, n2 in remove:
edges_to_n2 = 0
edges_from_n1 = len(edges[n1])
for e in edges:
if n2 in edges[e]:
edges_to_n2 += 1
if edges_to_n2 > 1 and edges_from_n1 > 1:
del edges[n1][n2]
for node_from in edges:
for node_to in edges[node_from]:
edge = {'from': node_from,
'to': node_to,
'value': edges[node_from][node_to]}
resp['graphResponse']['edges'].append(edge)
#print(resp)
return jsonify(resp)
@click.command()
@click.argument('concept_graph', type=click.Path(exists=True))
@click.argument('port', default=9898)
def main(concept_graph, port):
global cg
print('Reading concept graph:', end=' ')
cg = ConceptGraph(click.format_filename(concept_graph))
print('done.')
if os.path.exists('server.crt') and os.path.exists('server.key'):
context = ssl.SSLContext(ssl.PROTOCOL_TLSv1_2)
context.load_cert_chain('server.crt', 'server.key')
app.run(debug=True, host='0.0.0.0', port=port, ssl_context=context)
else:
app.run(debug=True, host='0.0.0.0', port=port)
if __name__ == '__main__':
main()