Skip to content

Commit 11679b7

Browse files
committed
[FEATURE] Add Consumer and Action to request metadata - LRN-48802
1 parent 532c40a commit 11679b7

File tree

5 files changed

+445
-59
lines changed

5 files changed

+445
-59
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,7 @@ Following are the routes to access our APIs.
119119
* Items API : http://localhost:8000/itemsapi
120120
* Reports API : http://localhost:8000/reportsapi
121121
* Question Editor API : http://localhost:8000/questioneditorapi
122+
* Data API : http://localhost:8000/dataapi
122123

123124
Open these pages with your web browser. These are all basic examples of Learnosity's integration. You can interact with these demo pages to try out the various APIs. The Items API example is a basic example of an assessment loaded into a web page with Learnosity's assessment player. You can interact with this demo assessment to try out the various Question types.
124125

docs/quickstart/assessment/standalone_assessment.py

Lines changed: 164 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,14 @@
55
# with `rendering_type: "assess"`.
66

77
# Include server side Learnosity SDK, and set up variables related to user access
8-
from learnosity_sdk.request import Init
8+
from learnosity_sdk.request import Init, DataApi
99
from learnosity_sdk.utils import Uuid
1010
from .. import config # Load consumer key and secret from config.py
1111
# Include web server and Jinja templating libraries.
1212
from http.server import BaseHTTPRequestHandler, HTTPServer
1313
from jinja2 import Template
14+
import json
15+
import os
1416

1517
# - - - - - - Section 1: Learnosity server-side configuration - - - - - - #
1618

@@ -32,13 +34,6 @@
3234
"domain": host,
3335
}
3436

35-
# Author Aide does not accept user_id so we need a separate security object
36-
authorAideSecurity = {
37-
"consumer_key": config.consumer_key,
38-
# Change to the domain used in the browser, e.g. 127.0.0.1, learnosity.com
39-
"domain": host,
40-
}
41-
4237
# Items API configuration parameters.
4338
items_request = {
4439
# Unique student identifier, a UUID generated above.
@@ -268,16 +263,6 @@
268263
}
269264
}
270265

271-
# Author Aide API configuration parameters.
272-
author_aide_request = {
273-
"user": {
274-
"id": 'python-demo-user',
275-
"firstname": 'Demos',
276-
"lastname": 'User',
277-
"email": '[email protected]'
278-
}
279-
}
280-
281266
# Set up Learnosity initialization data.
282267
initItems = Init(
283268
"items", security, config.consumer_secret,
@@ -304,18 +289,12 @@
304289
request = question_editor_request
305290
)
306291

307-
initAuthorAide = Init(
308-
"authoraide", authorAideSecurity, config.consumer_secret,
309-
request = author_aide_request
310-
)
311-
312292
# Generated request(initOptions) w.r.t all apis
313293
generated_request_Items = initItems.generate()
314294
generated_request_Questions = initQuestions.generate()
315295
generated_request_Author = initAuthor.generate()
316296
generated_request_Reports = initReports.generate()
317297
generated_request_QuestionEditor = initQuestionEditor.generate()
318-
generated_request_AuthorAide = initAuthorAide.generate()
319298

320299
# - - - - - - Section 2: your web page configuration - - - - - -#
321300

@@ -332,12 +311,36 @@ def createResponse(self, response: str) -> None:
332311

333312
def do_GET(self) -> None:
334313

314+
# Serve CSS file
315+
if self.path == "/css/style.css":
316+
try:
317+
# Get the directory where this script is located
318+
script_dir = os.path.dirname(os.path.abspath(__file__))
319+
# Navigate to the CSS file location
320+
css_path = os.path.join(script_dir, "..", "css", "style.css")
321+
322+
with open(css_path, 'r') as f:
323+
css_content = f.read()
324+
325+
self.send_response(200)
326+
self.send_header("Content-type", "text/css")
327+
self.end_headers()
328+
self.wfile.write(css_content.encode("utf-8"))
329+
return
330+
except FileNotFoundError:
331+
self.send_response(404)
332+
self.send_header("Content-type", "text/plain")
333+
self.end_headers()
334+
self.wfile.write(b"CSS file not found")
335+
return
336+
335337
if self.path.endswith("/"):
336338

337339
# Define the page HTML, as a Jinja template, with {{variables}} passed in.
338340
template = Template("""<!DOCTYPE html>
339341
<html>
340342
<head>
343+
<link rel="stylesheet" type="text/css" href="/css/style.css">
341344
<style>
342345
.tb { border-collapse: collapse; }
343346
.tb th, .tb td { padding: 5px; border: solid 1px #777; text-align: center;}
@@ -372,8 +375,8 @@ def do_GET(self) -> None:
372375
<td><a href="/questioneditorapi">Here</a></td>
373376
</tr>
374377
<tr>
375-
<td>Author Aide API</td>
376-
<td><a href="/authoraideapi">Here</a></td>
378+
<td>Data API</td>
379+
<td><a href="/dataapi">Here</a></td>
377380
</tr>
378381
</table>
379382
</body>
@@ -388,6 +391,9 @@ def do_GET(self) -> None:
388391
# Define the page HTML, as a Jinja template, with {{variables}} passed in.
389392
template = Template("""<!DOCTYPE html>
390393
<html>
394+
<head>
395+
<link rel="stylesheet" type="text/css" href="/css/style.css">
396+
</head>
391397
<body>
392398
<h1>{{ name }}</title></h1>
393399
<!-- Items API will render the assessment app into this div. -->
@@ -411,6 +417,9 @@ def do_GET(self) -> None:
411417
# Define the page HTML, as a Jinja template, with {{variables}} passed in.
412418
template = Template("""<!DOCTYPE html>
413419
<html>
420+
<head>
421+
<link rel="stylesheet" type="text/css" href="/css/style.css">
422+
</head>
414423
<body>
415424
<h1>{{ name }}</title></h1>
416425
<!-- Questions API will render here -->
@@ -432,6 +441,9 @@ def do_GET(self) -> None:
432441
# Define the page HTML, as a Jinja template, with {{variables}} passed in.
433442
template = Template("""<!DOCTYPE html>
434443
<html>
444+
<head>
445+
<link rel="stylesheet" type="text/css" href="/css/style.css">
446+
</head>
435447
<body>
436448
<h1>{{ name }}</title></h1>
437449
<!-- Author API will render here into the div -->
@@ -461,6 +473,9 @@ def do_GET(self) -> None:
461473
# Define the page HTML, as a Jinja template, with {{variables}} passed in.
462474
template = Template("""<!DOCTYPE html>
463475
<html>
476+
<head>
477+
<link rel="stylesheet" type="text/css" href="/css/style.css">
478+
</head>
464479
<body>
465480
<h1>{{ name }}</title></h1>
466481
<!-- Reports API will render into this span -->
@@ -490,6 +505,9 @@ def do_GET(self) -> None:
490505
# Define the page HTML, as a Jinja template, with {{variables}} passed in.
491506
template = Template("""<!DOCTYPE html>
492507
<html>
508+
<head>
509+
<link rel="stylesheet" type="text/css" href="/css/style.css">
510+
</head>
493511
<body>
494512
<h1>{{ name }}</title></h1>
495513
<!-- Question Editor API will render into this div. -->
@@ -519,28 +537,126 @@ def do_GET(self) -> None:
519537
response = template.render(name='Standalone Question Editor API Example', generated_request=generated_request_QuestionEditor)
520538
self.createResponse(response)
521539

522-
if self.path.endswith("/authoraideapi"):
523-
# Define the page HTML, as a Jinja template, with {{variables}} passed in.
524-
template = Template("""<!DOCTYPE html>
525-
<html>
526-
<body>
527-
<h1>{{ name }}</title></h1>
528-
<!-- Author Aide API will render into this div. -->
529-
<div id="aiApp"></div>
530-
<!-- Load the Author Aide API library. -->
531-
<script src=\"https://authoraide.learnosity.com\"></script>
532-
<!-- Initiate Author Aide API -->
533-
<script>
534-
var authorAideApp = LearnosityAuthorAide.init( {{ generated_request }}, '#aiApp' );
535-
</script>
536-
</body>
537-
</html>
538-
""")
539-
540-
# Render the page template and grab the variables needed.
541-
response = template.render(name='Author Aide API Example', generated_request=generated_request_AuthorAide)
542-
543-
self.createResponse(response)
540+
if self.path.endswith("/dataapi"):
541+
# Data API demo - retrieve items from the itembank
542+
template = Template("""<!DOCTYPE html>
543+
<html>
544+
<head>
545+
<link rel="stylesheet" type="text/css" href="/css/style.css">
546+
</head>
547+
<body>
548+
<h1>{{ name }}</h1>
549+
<p>This demo shows how to use the Data API to retrieve items from the Learnosity itembank.</p>
550+
551+
<div class="demo-section">
552+
<h2>Demo 1: Manual Iteration (5 items)</h2>
553+
<p>Using <code>request()</code> method with manual pagination via the 'next' pointer.</p>
554+
{{ demo1_output }}
555+
</div>
556+
557+
<div class="demo-section">
558+
<h2>Demo 2: Page Iteration (5 pages)</h2>
559+
<p>Using <code>request_iter()</code> method to automatically iterate over pages.</p>
560+
{{ demo2_output }}
561+
</div>
562+
563+
<div class="demo-section">
564+
<h2>Demo 3: Results Iteration (5 items)</h2>
565+
<p>Using <code>results_iter()</code> method to automatically iterate over individual items.</p>
566+
{{ demo3_output }}
567+
</div>
568+
569+
<p><a href="/">Back to API Examples</a></p>
570+
</body>
571+
</html>
572+
""")
573+
574+
# Run the Data API demos
575+
itembank_uri = 'https://data.learnosity.com/latest-lts/itembank/items'
576+
security_packet = {
577+
'consumer_key': config.consumer_key,
578+
'domain': host,
579+
}
580+
data_api = DataApi()
581+
582+
# Demo 1: Manual iteration
583+
demo1_html = ""
584+
try:
585+
data_request = {'limit': 1}
586+
for i in range(5):
587+
result = data_api.request(itembank_uri, security_packet,
588+
config.consumer_secret, data_request, 'get')
589+
response = result.json()
590+
591+
if response.get('data'):
592+
item = response['data'][0]
593+
demo1_html += f"""
594+
<div class="item">
595+
<div class="item-reference">Item {i+1}: {item.get('reference', 'N/A')}</div>
596+
<div class="item-status">Status: {item.get('status', 'N/A')}</div>
597+
</div>
598+
"""
599+
600+
if response.get('meta', {}).get('next'):
601+
data_request['next'] = response['meta']['next']
602+
else:
603+
break
604+
except Exception as e:
605+
demo1_html = f'<div class="error">Error: {str(e)}</div>'
606+
607+
# Demo 2: Page iteration
608+
demo2_html = ""
609+
try:
610+
data_request = {'limit': 1}
611+
page_count = 0
612+
for page in data_api.request_iter(itembank_uri, security_packet,
613+
config.consumer_secret, data_request, 'get'):
614+
page_count += 1
615+
demo2_html += f"""
616+
<div class="meta-info">
617+
Page {page_count}: {len(page.get('data', []))} items
618+
</div>
619+
"""
620+
if page.get('data'):
621+
for item in page['data']:
622+
demo2_html += f"""
623+
<div class="item">
624+
<div class="item-reference">{item.get('reference', 'N/A')}</div>
625+
<div class="item-status">Status: {item.get('status', 'N/A')}</div>
626+
</div>
627+
"""
628+
if page_count >= 5:
629+
break
630+
except Exception as e:
631+
demo2_html = f'<div class="error">Error: {str(e)}</div>'
632+
633+
# Demo 3: Results iteration
634+
demo3_html = ""
635+
try:
636+
data_request = {'limit': 1}
637+
result_count = 0
638+
for item in data_api.results_iter(itembank_uri, security_packet,
639+
config.consumer_secret, data_request, 'get'):
640+
result_count += 1
641+
demo3_html += f"""
642+
<div class="item">
643+
<div class="item-reference">Item {result_count}: {item.get('reference', 'N/A')}</div>
644+
<div class="item-status">Status: {item.get('status', 'N/A')}</div>
645+
<pre>{json.dumps(item, indent=2)[:500]}...</pre>
646+
</div>
647+
"""
648+
if result_count >= 5:
649+
break
650+
except Exception as e:
651+
demo3_html = f'<div class="error">Error: {str(e)}</div>'
652+
653+
response = template.render(
654+
name='Data API Example',
655+
demo1_output=demo1_html,
656+
demo2_output=demo2_html,
657+
demo3_output=demo3_html
658+
)
659+
self.createResponse(response)
544660

545661
def main() -> None:
546662
web_server = HTTPServer((host, port), LearnosityServer)

0 commit comments

Comments
 (0)