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
99from learnosity_sdk .utils import Uuid
1010from .. import config # Load consumer key and secret from config.py
1111# Include web server and Jinja templating libraries.
1212from http .server import BaseHTTPRequestHandler , HTTPServer
1313from jinja2 import Template
14+ import json
15+ import os
1416
1517# - - - - - - Section 1: Learnosity server-side configuration - - - - - - #
1618
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.
4338items_request = {
4439 # Unique student identifier, a UUID generated above.
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- 278- }
279- }
280-
281266# Set up Learnosity initialization data.
282267initItems = Init (
283268 "items" , security , config .consumer_secret ,
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
313293generated_request_Items = initItems .generate ()
314294generated_request_Questions = initQuestions .generate ()
315295generated_request_Author = initAuthor .generate ()
316296generated_request_Reports = initReports .generate ()
317297generated_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
545661def main () -> None :
546662 web_server = HTTPServer ((host , port ), LearnosityServer )
0 commit comments