<?xml version="1.0" encoding="utf-8"?><?xml-stylesheet type="text/xsl" href="atom.xsl"?>
<feed xmlns="http://www.w3.org/2005/Atom">
    <id>https://docs.e-rwu.biz/pl/blog</id>
    <title>Moorlands DigiFolio™ Blog</title>
    <updated>2026-12-12T00:00:00.000Z</updated>
    <generator>https://github.com/jpmonette/feed</generator>
    <link rel="alternate" href="https://docs.e-rwu.biz/pl/blog"/>
    <subtitle>Moorlands DigiFolio™ Blog</subtitle>
    <icon>https://docs.e-rwu.biz/pl/img/favicon.ico</icon>
    <entry>
        <title type="html"><![CDATA[v1.99 Longer sample]]></title>
        <id>https://docs.e-rwu.biz/pl/blog/long-blog-post</id>
        <link href="https://docs.e-rwu.biz/pl/blog/long-blog-post"/>
        <updated>2026-12-12T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[This is the summary of a very long blog post,]]></summary>
        <content type="html"><![CDATA[<p>This is the summary of a very long blog post,</p>
<p>Use a <code>&lt;!--</code> <code>truncate</code> <code>--&gt;</code> comment to limit blog post size in the list view.</p>
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet</p>
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet</p>
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet</p>
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet</p>
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet</p>
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet</p>
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet</p>
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet</p>
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet</p>
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet</p>
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet</p>
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet</p>
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet</p>
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet</p>
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet</p>
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet</p>]]></content>
        <author>
            <name>John Renfrew</name>
            <uri>https://linkedin.com/in//john-renfrew-81780614</uri>
        </author>
        <category label="Hello" term="Hello"/>
        <category label="Docusaurus" term="Docusaurus"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[v1.1.8 Playbook]]></title>
        <id>https://docs.e-rwu.biz/pl/blog/v1.1.8</id>
        <link href="https://docs.e-rwu.biz/pl/blog/v1.1.8"/>
        <updated>2026-03-22T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[Version 1.1.8 documentation release]]></summary>
        <content type="html"><![CDATA[<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="version-118-documentation-release">Version 1.1.8 documentation release<a href="https://docs.e-rwu.biz/pl/blog/v1.1.8#version-118-documentation-release" class="hash-link" aria-label="Bezpośredni link do Version 1.1.8 documentation release" title="Bezpośredni link do Version 1.1.8 documentation release" translate="no">​</a></h3>
<ul>
<li class="">Naming strategy for tasks and groupings of tasks</li>
<li class="">Playbook release for comment</li>
</ul>
<p>The most recent iteration of the playbook (0.2b) has suggestions for the terminology required for task creation and assignment, after a test FM file was created to look at the relationships that would be needed to provide a re-usable interface for getting from a single definition of a task to the individual set that a student needs to interact with on a placement.</p>
<p>The <code>Task</code> creation and entry has two parts, the creation of a surveyjs template saved as JSON, which describes fully the form of questions that will be presented to a student, and the metadata about which module(s), level, course which apply to this - along with some additional flags. The record will show an isActive state, and a date of introduction and also a date of retirement. Once the current date has passed either the introduction or retirement date, the active flag will update.</p>
<p>These tasks may be gathered into a <code>TaskSet</code>, as a convenience holder for multiple similar or parallel Tasks to be carried forwards together. This a re-usable short-hand designed to bring soem time-saving with maximum flexibility. These can be a named group to assist Admins to gather all the tasks that will be required in the next step.</p>
<p>A <code>TaskScheme</code> is a year-scoped record which builds a list of the taskIDs defined by linking to one or more TaskSet records and individual Tasks. Because there is a requirement for all Placements to complete a <code>PlacementExpectation</code>, there will be a method to select from the relevant possibilities. This scheme will have an action button to generate child <code>YearSet</code> records as a one-to-one match for all the referenced Task records linked to the TaskScheme. These will contain a store of deadline dates for each activity for each of the study modes/centres. These form the default date when later instantiated for the Student.</p>
<p>When a student is assigned a Placment record for the current year, the correct TaskScheme link is applied, and all of its YearSet records are used to create all of the <code>StudentTask</code> records that reveal which activities a Student needs to complete during the Placement. These are created with the relevant deadline data from the YearScheme record.</p>
<p>Part of the creation of the records may involve some schema which defines the onward routes allowed - defining who may interact with the record next, and whether or not it is allowable to resubmit the whole thing. It is key that this does not change any already set late flags but will allow the record to return to an 'open' state. This may involve additional stamp fields, but definitely requires audit logging of the action.</p>
<p>Archtitecture is therefore <strong><code>T</code> → <code>TP</code> → <code>TS</code> → <code>YS</code> → <code>ST</code></strong></p>
<p>Interactions are Role driven, and these have been defined and enumerated for:</p>
<ul>
<li class="">Student</li>
<li class="">Supervisor</li>
<li class="">Observer</li>
<li class="">Marker</li>
<li class="">Moderator</li>
<li class="">Admin</li>
</ul>]]></content>
        <author>
            <name>John Renfrew</name>
            <uri>https://linkedin.com/in//john-renfrew-81780614</uri>
        </author>
        <category label="Documentation" term="Documentation"/>
        <category label="Revision" term="Revision"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[v1.1.7]]></title>
        <id>https://docs.e-rwu.biz/pl/blog/v1.1.7</id>
        <link href="https://docs.e-rwu.biz/pl/blog/v1.1.7"/>
        <updated>2026-03-20T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[Version 1.1.7 internal test release]]></summary>
        <content type="html"><![CDATA[<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="version-117-internal-test-release">Version 1.1.7 internal test release<a href="https://docs.e-rwu.biz/pl/blog/v1.1.7#version-117-internal-test-release" class="hash-link" aria-label="Bezpośredni link do Version 1.1.7 internal test release" title="Bezpośredni link do Version 1.1.7 internal test release" translate="no">​</a></h3>
<ul>
<li class="">Writing playbooks</li>
<li class="">New server</li>
</ul>
<p>Many of the core technology tests or pipeline requirements are now encapsulated into playbook markdown files. These serve the dual purpose of documenting test assumptions and results, and keeping a record of open or unanswered questions. They will also provide a valuable resource for the eventual documentation of the structure behind production build code.</p>
<p>The most important of these under devlopment, is the <code>DigiFolio_Student,</code> which decribes the interactions with the app from the position of the various defined roles. It also covers the core principles behind how the Student Taksks relate to Placements and Modules. These playbooks enable assumptions to be questioned in advance of writing code, as it is 'cheap' to make amendments at this planning stage, and much more difficult later.</p>
<p>Now that Oracle have stopped being difficult to deal with, there is a dedicated FileMaker server for this project. It is reached at folio.moorlands.ac.uk. This means we can start to scope out the architecture of file relationships, especially communication between Folio FileMaker files and Sopley Central. Testing and build can now proceed with final urls and account settings even during test phases. There is a need for a register of 'users' to feed the Clerk auth, and replicated data on placements.</p>]]></content>
        <author>
            <name>John Renfrew</name>
            <uri>https://linkedin.com/in//john-renfrew-81780614</uri>
        </author>
        <category label="Test" term="Test"/>
        <category label="Revision" term="Revision"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[v1.1.6]]></title>
        <id>https://docs.e-rwu.biz/pl/blog/v1.1.6</id>
        <link href="https://docs.e-rwu.biz/pl/blog/v1.1.6"/>
        <updated>2026-03-15T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[Version 1.1.6 internal test release]]></summary>
        <content type="html"><![CDATA[<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="version-116-internal-test-release">Version 1.1.6 internal test release<a href="https://docs.e-rwu.biz/pl/blog/v1.1.6#version-116-internal-test-release" class="hash-link" aria-label="Bezpośredni link do Version 1.1.6 internal test release" title="Bezpośredni link do Version 1.1.6 internal test release" translate="no">​</a></h3>
<ul>
<li class="">Testing video upload and precessing</li>
</ul>
<p>Following from conversation about StudentTasks, a technology test was completed to uncover issues and benefits in the requirement to allow video uploads, with later playback (markers and external). In the same way as documents, this needs to allow the re-submission of a file until the task is 'locked'.</p>
<p>Because the upload can be a long running process, then upload status needs to be visible while it is happening, and then encoding to straming formats takes place service-side, and so there is a requirement for the UI to show the stages of progress up to the point of completion. From that point, with the record in locked state the UI would show both a thumbnnail of the video, and button elements to view the video using the service player.</p>
<p>At this stage, all testing was done with a small (32sec) video, but later testing needs to work out timings for upload, encoding, and the storage implications of a years worth of full-size video.</p>
<p>After previous investigation conclusions, this test was done with Bunny -<code>https://bunny.net</code>. Their Stream platform is fully API driven and so highly suitable for the purpose. A playbook is written and fully tested.</p>
<ul>
<li class="">Upload → first creates BunnyStream video object, returns videoId (similar to Inspera)</li>
<li class="">On return the StudentTask record is patched with this into videoID field and status of uploading</li>
<li class="">Upload → then the file is uploaded to that document record, using <strong>TUS</strong> resumable upload protocol (automaticlly handles interrupted transfers)</li>
<li class="">On status complete, the StudentTaks record is updated to show that processing is processing</li>
<li class="">MetaTags are added to the Stream record, which can be used for search and other API operations</li>
<li class="">Task record videoStatus updated through lifecycle, videoEmbedUrl populated when encoding complete</li>
<li class="">Bunny sends webhooks on progress and these are routed through Claris Connect, so that some actions can eaily update Task record, and also forwarded to the node service</li>
</ul>
<p>Direct upload to Bunny ensure video bytes never touch our server, we only handle metadata and progress or status information. The player format is simple &gt; <a href="https://player.mediadelivery.net/embed/%7BlibraryId%7D/%7BvideoId%7D" target="_blank" rel="noopener noreferrer" class="">https://player.mediadelivery.net/embed/{libraryId}/{videoId}</a></p>
<p>Investigation was made into using signed links for video retrieval but honestly wasted a lot of time on something which still unable to get to work. The files ar enot publicly exposed and the ony way you can get to view is from inside something where you are logged in, so for the moment parked unless there is deemed a need. The player's default behaviours (autoplay, captions, preload) can be set globally in the BunnyStream library dashboard.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="encoding">Encoding<a href="https://docs.e-rwu.biz/pl/blog/v1.1.6#encoding" class="hash-link" aria-label="Bezpośredni link do Encoding" title="Bezpośredni link do Encoding" translate="no">​</a></h3>
<p>Encoding is free on the strndard tier but uses a shared queue. Premium encoding is available for a charge : uses Just-In-Time (JIT) technology — video playable within seconds of upload. Cost at $0.05/minute: estimate of max 1,200 minutes × $0.05 = $60/year maximum. Given that the marker is looking at the task records after they are completed it is unlikley that this would be required.</p>
<p>Testing with a 32-second 64MB MOV file completed end-to-end in under 6 minutes, testing with more realistic file will give better benchmarks. This does affect the ability of the student to view the uploaded and encoded file, discussion needed.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="futher-development">Futher development<a href="https://docs.e-rwu.biz/pl/blog/v1.1.6#futher-development" class="hash-link" aria-label="Bezpośredni link do Futher development" title="Bezpośredni link do Futher development" translate="no">​</a></h3>
<p>A student on a mobile device (e.g. recording placement footage on a phone) could receive an SMS or email containing a link to a minimal, standalone upload page from inside the Digifolio. The link contains enough context to identify the StudentTask record without requiring full portal authentication, to be able to view the video. This would be linked to a one-time or short-lived token.</p>]]></content>
        <author>
            <name>John Renfrew</name>
            <uri>https://linkedin.com/in//john-renfrew-81780614</uri>
        </author>
        <category label="Test" term="Test"/>
        <category label="Revision" term="Revision"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[v1.1.5]]></title>
        <id>https://docs.e-rwu.biz/pl/blog/v1.1.5</id>
        <link href="https://docs.e-rwu.biz/pl/blog/v1.1.5"/>
        <updated>2026-03-13T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[Version 1.1.5 internal test release]]></summary>
        <content type="html"><![CDATA[<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="version-115-internal-test-release">Version 1.1.5 internal test release<a href="https://docs.e-rwu.biz/pl/blog/v1.1.5#version-115-internal-test-release" class="hash-link" aria-label="Bezpośredni link do Version 1.1.5 internal test release" title="Bezpośredni link do Version 1.1.5 internal test release" translate="no">​</a></h3>
<ul>
<li class="">Testing .docx and .pdf text extraction</li>
</ul>
<p>Following from conversation about StudentTasks, a technology test was completed to create a service to be able to ansyncronously retrieve a base64 encoded document from a FileMaker record. This would be triggered after a <code>submit</code> action finalises a student task submission. There is no UI requirement, so status information becomes key to confirm completion, failure or stalled extractions. Paramters are <code>{taskID, fileType}</code></p>
<div class="language-json codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-json codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token property" style="color:#36acaa">"text"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain">       </span><span class="token string" style="color:#e3116c">"extracted plain text content"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token property" style="color:#36acaa">"charCount"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain">  </span><span class="token number" style="color:#36acaa">17961</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token property" style="color:#36acaa">"durationMs"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">209</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token property" style="color:#36acaa">"library"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain">    </span><span class="token string" style="color:#e3116c">"mammoth"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token property" style="color:#36acaa">"version"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain">    </span><span class="token string" style="color:#e3116c">"1.8.0"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token property" style="color:#36acaa">"filename"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain">   </span><span class="token string" style="color:#e3116c">"report.docx"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token property" style="color:#36acaa">"lineBreaks"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"single"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token property" style="color:#36acaa">"hash"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain">       </span><span class="token string" style="color:#e3116c">"c99b32954d120bf62ad818944660275f"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre></div></div>
<p>This has been extended to provide a micro-service, which can take parameters of type and base64encoded file, along with fileName and returnHash. As this is an open endpoint we shall be adding a state or session parameter to reduce fake attempts.</p>
<div class="theme-admonition theme-admonition-info admonition_xJq3 alert alert--info"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 14 16"><path fill-rule="evenodd" d="M7 2.3c3.14 0 5.7 2.56 5.7 5.7s-2.56 5.7-5.7 5.7A5.71 5.71 0 0 1 1.3 8c0-3.14 2.56-5.7 5.7-5.7zM7 1C3.14 1 0 4.14 0 8s3.14 7 7 7 7-3.14 7-7-3.14-7-7-7zm1 3H6v5h2V4zm0 6H6v2h2v-2z"></path></svg></span>informacja</div><div class="admonitionContent_BuS1"><p><code>cURL https://server/extraction/api/extract/direct</code>
<code>{"b64": "${B64}", "fileType": "pdf", "fileName": "test4.pdf", "returnHash": true}</code></p><div class="language-json codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-json codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token property" style="color:#36acaa">"text"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"extracted text"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token property" style="color:#36acaa">"charCount"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">2115</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token property" style="color:#36acaa">"durationMs"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">241</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token property" style="color:#36acaa">"library"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"mammoth"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token property" style="color:#36acaa">"version"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"1.12.0"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token property" style="color:#36acaa">"filename"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"test.docx"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token property" style="color:#36acaa">"hash"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"c99b32954d120bf62ad818944660275f"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre></div></div></div></div>
<p>Response is very fast at sub 300ms for 4 page test docx file, and this covers:</p>
<ul>
<li class="">TLS handshake</li>
<li class="">nginx proxy overhead and routing</li>
<li class="">base64 decode</li>
<li class="">mammoth parsing the DOCX XML</li>
<li class="">JSON serialisation of the response</li>
<li class="">network latency both ways</li>
</ul>
<p>A playbook is written and fully tested.</p>
<ul>
<li class="">Upload → stores b64 of doc on StudentTask record</li>
<li class="">(may repeat — each upload overwrites previous b64)</li>
<li class="">Commit → OData PATCH (answers + timestamp + locked)</li>
<li class="">→ fire-and-forget POST to extraction service</li>
<li class="">POST /api/extract</li>
</ul>
<ul>
<li class="">Node Extraction Service (Express)</li>
</ul>
<ol>
<li class="">PATCH StudentTask → extractionStatus: 'processing'</li>
<li class="">Fetch b64 from StudentTask via OData</li>
<li class="">Decode → Buffer</li>
<li class="">Branch: DOCX → mammoth  |  PDF → pdfjs-dist</li>
<li class="">Compute MD5 hash of original binary</li>
<li class="">PATCH StudentTask → extracted text + metadata</li>
<li class="">POST to FileMaker script → archive b64 to Documents
FileMaker
StudentTask record — extracted text, status fields
Documents record  — reconstituted original binary
linked to StudentTask</li>
</ol>
<p>If questions are prefixed with a known character (§) then the text can be extracted with singke carriage returns and then substitute extra lines before teh character for presentation purposes. The extraction is written as a service, so could be called from other places in the FileMaker ecosphere.</p>]]></content>
        <author>
            <name>John Renfrew</name>
            <uri>https://linkedin.com/in//john-renfrew-81780614</uri>
        </author>
        <category label="Test" term="Test"/>
        <category label="Revision" term="Revision"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[v1.1.4 Translation]]></title>
        <id>https://docs.e-rwu.biz/pl/blog/v1.1.4</id>
        <link href="https://docs.e-rwu.biz/pl/blog/v1.1.4"/>
        <updated>2026-03-11T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[Version 1.1.4 internal test release]]></summary>
        <content type="html"><![CDATA[<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="version-114-internal-test-release">Version 1.1.4 internal test release<a href="https://docs.e-rwu.biz/pl/blog/v1.1.4#version-114-internal-test-release" class="hash-link" aria-label="Bezpośredni link do Version 1.1.4 internal test release" title="Bezpośredni link do Version 1.1.4 internal test release" translate="no">​</a></h3>
<ul>
<li class="">Translations tests to docs site</li>
<li class="">Polish (pl)</li>
<li class="">Hungarian (hu)</li>
<li class="">Romanian (ro)</li>
<li class="">Serbian (hr)</li>
</ul>
<p>Added frameworks and set up for aditional translation support for Supervisor tutotrial pages. Basic supervisor and student docs pages are now in languages.</p>
<p>Decision needed on which Serbo-Croation flavours to support.</p>
<p>Installed the <code>AWS</code> translation code into Sopley_portfolio on the altdb server, to make it more widely available. Paste the text to be translated into the left field, select the language for the output text, and push the translate button. Once the translation is returned, the copy button will place the output text on the clipboard. You may, of course, copy parts of the output.</p>
<p>There is an indication of the length of the text in the centre, along with an indication of the cost of the action. AWS charges $50 per million characters.</p>
<!-- -->
<img src="https://docs.e-rwu.biz/pl/assets/images/translate_00-6e40734f3807125e35133560cf888bb9.png" alt="portfolio" style="width:800px">]]></content>
        <author>
            <name>John Renfrew</name>
            <uri>https://linkedin.com/in//john-renfrew-81780614</uri>
        </author>
        <category label="Test" term="Test"/>
        <category label="Revision" term="Revision"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[v1.1.3 Tasks interface]]></title>
        <id>https://docs.e-rwu.biz/pl/blog/v1.1.3</id>
        <link href="https://docs.e-rwu.biz/pl/blog/v1.1.3"/>
        <updated>2026-03-03T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[Version 1.1.3 internal test release]]></summary>
        <content type="html"><![CDATA[<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="version-113-internal-test-release">Version 1.1.3 internal test release<a href="https://docs.e-rwu.biz/pl/blog/v1.1.3#version-113-internal-test-release" class="hash-link" aria-label="Bezpośredni link do Version 1.1.3 internal test release" title="Bezpośredni link do Version 1.1.3 internal test release" translate="no">​</a></h3>
<ul>
<li class="">progress on scope documentation, which is mostly complete and released as v1.0</li>
<li class="">inital clickable demo on the Tasks interface</li>
<li class="">starter for discussion on how some of the parts need to work with each other</li>
</ul>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="tasks-interface">Tasks interface<a href="https://docs.e-rwu.biz/pl/blog/v1.1.3#tasks-interface" class="hash-link" aria-label="Bezpośredni link do Tasks interface" title="Bezpośredni link do Tasks interface" translate="no">​</a></h3>
<p>This is a pseudo-live clickable demo, the code equivalent of wireframes, which serves as a design exploration, decision tree, and technology test all-on-one. From the placment list this outlines the behaviour after a students selects <code>Tasks</code>.</p>
<p>A list of <strong>Tasks</strong> is presented, with indicators for deadline date, late, and locked. If a user selects a task row, it replaces the placement list with the survey view <strong>in place</strong> — no page navigation, top navigation stays but back nav button changes label and action. If there is an stored data returned from the query initiated on clicking the row, client code <strong>always injects authoritative values</strong> for these fields, overriding anything in locally stored data.</p>
<!-- -->
<img src="https://docs.e-rwu.biz/pl/assets/images/tasks_01-952d00cf6af2845739e9d2b6e0830573.png" alt="portfolio" style="width:800px">
<p>The student enters data or selects form a presented choice, and at the footer are three buttons. Save will perform and explicit commit of data (back to FileMaker). Clear will remove all current values from form. Submit will show dialog confirming action, and then both save data and add the finalCommit value. For students, all buttons are hidden when <code>isLocked === true</code>.</p>
<!-- -->
<img src="https://docs.e-rwu.biz/pl/assets/images/tasks_00-301dc384f74ba2ab4dd5e4f637d9e436.png" alt="portfolio" style="width:800px">
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="autosave">Autosave<a href="https://docs.e-rwu.biz/pl/blog/v1.1.3#autosave" class="hash-link" aria-label="Bezpośredni link do Autosave" title="Bezpośredni link do Autosave" translate="no">​</a></h4>
<p>The Student + open state only — record is debounced at 1500ms after last keystroke to trigger autosave. No autosave for supervisor, marker, or any finalised record. (autocheck needs to only be triggered if there is data, so after clear, it will not overwrite savedData ??)</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="surveyjson-hidden-fields">SurveyJson Hidden Fields<a href="https://docs.e-rwu.biz/pl/blog/v1.1.3#surveyjson-hidden-fields" class="hash-link" aria-label="Bezpośredni link do SurveyJson Hidden Fields" title="Bezpośredni link do SurveyJson Hidden Fields" translate="no">​</a></h3>
<ul>
<li class=""><code>studentID</code>, <code>moduleID</code>, <code>superID</code> remain as hidden questions inside the surveyJson</li>
<li class="">This preserves the FileMaker Creator approval workflow — question authors can test conditions without code</li>
</ul>
<p>Because this is a combination of React and SurveyJS, there are behaviours that can be driven from the client as well as the JSON definition. They work at different levels: SurveyJS JSON conditions handle question-level behaviour, React/client code handles the survey-level behaviour in TaskClient.tsx - this the important one for the locked/finalised case (this needs to be always for external, and for student if locked) — which puts the entire SurveyJS instance into a read-only display mode regardless of any individual question conditions. No inputs are rendered, everything becomes plain text.</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">if (isStudent &amp;&amp; task.state !== "locked" || isExternal) {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  survey.mode = "display";</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">}</span><br></span></code></pre></div></div>
<p>The desired behaviour for  supervisor and marker requirements need finalising, and a decision about visibility to students of supervisor and/or marker comments.<br>
A <strong>TEMPLATE</strong> Task is created, with one of each role of question and notes about visibility conditions. Creators start from there and duplicate questions as required, which keeps the settings &amp; conditions. This is easy because you can simply change th question type from the drop-down.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="question-naming-conventions">Question Naming Conventions<a href="https://docs.e-rwu.biz/pl/blog/v1.1.3#question-naming-conventions" class="hash-link" aria-label="Bezpośredni link do Question Naming Conventions" title="Bezpośredni link do Question Naming Conventions" translate="no">​</a></h3>
<table><thead><tr><th>Prefix</th><th>Who</th><th>Behaviour</th></tr></thead><tbody><tr><td><code>Q1</code>, <code>Q2</code> etc</td><td>Student questions</td><td><code>enableIf: "{superID} empty"</code></td></tr><tr><td><code>SQ1</code>, <code>SQ2</code> etc</td><td>Supervisor questions</td><td><code>enableIf: "markerID} empty", visibleIf: "{superID} notempty"</code></td></tr><tr><td><code>MQ1</code>, <code>MQ2</code> etc</td><td>Marker questions</td><td><code>visibleIf: "{markerID} notempty"</code></td></tr><tr><td><code>studentID</code>, <code>moduleID</code>, <code>superID</code>, <code>markerID</code></td><td>Hidden carriers</td><td>Always injected by client</td></tr></tbody></table>]]></content>
        <author>
            <name>John Renfrew</name>
            <uri>https://linkedin.com/in//john-renfrew-81780614</uri>
        </author>
        <category label="Test" term="Test"/>
        <category label="Revision" term="Revision"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[v1.1.2]]></title>
        <id>https://docs.e-rwu.biz/pl/blog/v1.1.2</id>
        <link href="https://docs.e-rwu.biz/pl/blog/v1.1.2"/>
        <updated>2026-03-02T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[Version 1.1.2 internal test release]]></summary>
        <content type="html"><![CDATA[<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="version-112-internal-test-release">Version 1.1.2 internal test release<a href="https://docs.e-rwu.biz/pl/blog/v1.1.2#version-112-internal-test-release" class="hash-link" aria-label="Bezpośredni link do Version 1.1.2 internal test release" title="Bezpośredni link do Version 1.1.2 internal test release" translate="no">​</a></h3>
<ul>
<li class="">Echo360 API</li>
<li class="">Updated SurveyJS to 2.5.13</li>
<li class="">more</li>
</ul>
<p>Echo360 has an API, which allows us to manage User accounts (and media). We have tested the API, and have scripts running in FileMaker to query Student users, based on Moorlands Email address. There is also a script to create a user, and Echo then sends them an email invite to the platform. We need to investigate what happens with the MCEE students and time zones on sign-up.</p>
<p>The access token for the API is valid for 1 hour, after which the refresh token can be used to retrieve another, along with a new refresh token. Each refresh token may only be used once, so it there is some failure, the process needs to use the client-credentials workflow as a fallback.</p>
<p>The creation of the <strong>Echo360</strong> account could be scripted as part of the initial student comms from Technical Support. With API access, the User accounts could then be removed as part of the end-of year or leaving process.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="echo360-video">Echo360 Video<a href="https://docs.e-rwu.biz/pl/blog/v1.1.2#echo360-video" class="hash-link" aria-label="Bezpośredni link do Echo360 Video" title="Bezpośredni link do Echo360 Video" translate="no">​</a></h3>
<p>It may be that using the User account to hook to any video uploads is not the right thing to do, as the Student:</p>
<ul>
<li class="">would have to manually create a sharing link so that it can be pasted</li>
<li class="">has the opportunity to delete the file after upload</li>
</ul>
<p>Next investigations will cover the mechanics of uploads through the DigiFolio, but sent to a holding account, which ensures that students can not see anyone else's work (but also their own)</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="the-core-problem">The Core Problem<a href="https://docs.e-rwu.biz/pl/blog/v1.1.2#the-core-problem" class="hash-link" aria-label="Bezpośredni link do The Core Problem" title="Bezpośredni link do The Core Problem" translate="no">​</a></h3>
<p>The Echo360 public API (/public/api/v1/medias) does not expose raw file download URLs for media. The /medias endpoint returns metadata about media items (title, ID, duration, owner, status, etc.), but there is no field in the response that gives you a direct link to the underlying MP4 file. Echo360's architecture deliberately abstracts away the raw files — they're stored in S3 behind authenticated, signed URLs that are generated on-the-fly by the player, not exposed through the public API.</p>
<p>There are plenty of alternatives for S3 compatible storage, which could provide a signed (time-limited) download link, but the better option might be through a site that offers a stremaing viewer, which also implies a need for encoding. Alternative to investigate is <a href="https://bunny.net/pricing/stream/" target="_blank" rel="noopener noreferrer" class="">BunnyStream</a>. The pricing for this is micropriced, based on storage volume and playback traffic, so needs some research into the projected volumes.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="surveyjs">SurveyJS<a href="https://docs.e-rwu.biz/pl/blog/v1.1.2#surveyjs" class="hash-link" aria-label="Bezpośredni link do SurveyJS" title="Bezpośredni link do SurveyJS" translate="no">​</a></h3>
<p>We have also updated SurveyJS to the latest version, 2.5.13, which includes some bug fixes and performance improvements. We have tested the new version with our existing surveys, and everything seems to be working fine.<br>
We have also made some minor revisions and corrections to our documentation specifications and codebase, which should improve the overall stability and usability of the system.</p>]]></content>
        <author>
            <name>John Renfrew</name>
            <uri>https://linkedin.com/in//john-renfrew-81780614</uri>
        </author>
        <category label="Test" term="Test"/>
        <category label="Revision" term="Revision"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[v1.1.1]]></title>
        <id>https://docs.e-rwu.biz/pl/blog/v1.1.1</id>
        <link href="https://docs.e-rwu.biz/pl/blog/v1.1.1"/>
        <updated>2026-03-01T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[Local layouts to copy web app]]></summary>
        <content type="html"><![CDATA[<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="local-layouts-to-copy-web-app">Local layouts to copy web app<a href="https://docs.e-rwu.biz/pl/blog/v1.1.1#local-layouts-to-copy-web-app" class="hash-link" aria-label="Bezpośredni link do Local layouts to copy web app" title="Bezpośredni link do Local layouts to copy web app" translate="no">​</a></h3>
<p>Investigating how the elements that are web native can be replicated in FileMaker. It is not the same thing, so either we find a way to adapt the web code to show in a single page webviewer or mimic the layout but accept there are some differences in function.</p>
<p>The webcode is not pure js or html, and has a lot of restrictions around login, so feels really too much work to reconstruct just the display elements. The icons from react-lucide are available as SVG downloads, so after some manipulation can be used to match the web UI.</p>
<p>Some old-school tricks here to get highlighting and button visibility to work, but this is (native) FileMaker:</p>
<!-- -->
<img src="https://docs.e-rwu.biz/pl/assets/images/fmp_00-b60d2c810595840dd0ab73324e22c46c.png" alt="portfolio" style="width:600px">]]></content>
        <author>
            <name>John Renfrew</name>
            <uri>https://linkedin.com/in//john-renfrew-81780614</uri>
        </author>
        <category label="Test" term="Test"/>
        <category label="Revision" term="Revision"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[v1.1.0 StudentTask definition]]></title>
        <id>https://docs.e-rwu.biz/pl/blog/v1.1.0</id>
        <link href="https://docs.e-rwu.biz/pl/blog/v1.1.0"/>
        <updated>2026-02-28T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[Definition of StudentTasks]]></summary>
        <content type="html"><![CDATA[<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="definition-of-studenttasks">Definition of StudentTasks<a href="https://docs.e-rwu.biz/pl/blog/v1.1.0#definition-of-studenttasks" class="hash-link" aria-label="Bezpośredni link do Definition of StudentTasks" title="Bezpośredni link do Definition of StudentTasks" translate="no">​</a></h3>
<p>This article assembles the tech tests undertaken, and review notes for investigations inot the best mechanisms to implement the <strong>SurevyJS</strong> code for <code>StudentTasks</code> within the DigiFolio application under development. While these are presented as a structured document, and might appear as finalised, it is also for the purpose of discussion of the key decisions represented, to ensure that the right architectural direction is taken.</p>
<p>The core of this document is saved as /</p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="studenttask-workflow--implementation">StudentTask Workflow &amp; Implementation<a href="https://docs.e-rwu.biz/pl/blog/v1.1.0#studenttask-workflow--implementation" class="hash-link" aria-label="Bezpośredni link do StudentTask Workflow &amp; Implementation" title="Bezpośredni link do StudentTask Workflow &amp; Implementation" translate="no">​</a></h2>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="overview">Overview<a href="https://docs.e-rwu.biz/pl/blog/v1.1.0#overview" class="hash-link" aria-label="Bezpośredni link do Overview" title="Bezpośredni link do Overview" translate="no">​</a></h3>
<p>This document presents the current design patterns and implementation decisions for the <strong>StudentTasks</strong>, including multi-role handling, autosave, final submission concept, and review workflows.</p>
<p><strong>Actors</strong>:</p>
<ul>
<li class=""><strong>Student</strong> – completes one or more survey questions (Task)</li>
<li class=""><strong>Supervisor</strong> – reviews and optionally adds comments</li>
<li class=""><strong>Marker</strong> – oversight and comments specifically at mid-year/end-of-year</li>
<li class=""><strong>External Examiner</strong> – audits combined data from selected student task records</li>
</ul>
<p><strong>Design Principles</strong>:</p>
<ul>
<li class="">Separation of <strong>student answers</strong> (<code>savedData</code>) and <strong>reviewer/marker comments</strong> (<code>reviewerData</code>, <code>markerData</code>)</li>
<li class="">Finalised answers pushed to additional log file for audit/ academic governance</li>
<li class="">Autosave only for <strong>students</strong></li>
<li class="">Final submission triggers addition of <strong>timestamp</strong> and <strong>webhook audit/export</strong></li>
<li class="">Short reviewer/ marker questions do not require autosave; manual submission only</li>
</ul>
<p>note: marker/tutor may end up with discrete set of tasks fitted into a student task list, and discussion is needed about additional marker interaction with individual StudentTasks.</p>
<hr>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="field-definitions">Field Definitions<a href="https://docs.e-rwu.biz/pl/blog/v1.1.0#field-definitions" class="hash-link" aria-label="Bezpośredni link do Field Definitions" title="Bezpośredni link do Field Definitions" translate="no">​</a></h3>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="source-filemaker-records"><em>Source: FileMaker records</em><a href="https://docs.e-rwu.biz/pl/blog/v1.1.0#source-filemaker-records" class="hash-link" aria-label="Bezpośredni link do source-filemaker-records" title="Bezpośredni link do source-filemaker-records" translate="no">​</a></h4>
<table><thead><tr><th>Field</th><th>Purpose</th><th>Update Rules</th></tr></thead><tbody><tr><td><code>savedData</code> (JSON)</td><td>Student task answers</td><td>Updated on Save or Finalise</td></tr><tr><td><code>lastSubmit</code></td><td>Most recent save timestamp</td><td>Updated every save</td></tr><tr><td><code>finalSubmit</code></td><td>Timestamp of final submission</td><td>Updated only on Finalise</td></tr><tr><td><code>status</code> (enum)</td><td>Workflow stage: 0=Open/Active, 1=Submitted, 2=Viewed, 3=Reviwed, 4=Marked?, 5=Examined</td><td>Updated only on Finalise or Submit button by Super, Marker, External</td></tr><tr><td><code>isLate</code></td><td>Derived from <code>finalSubmit &gt; deadlineDate</code></td><td>Auto-calculated</td></tr><tr><td><code>superData</code> (JSON)</td><td>Supervisor/Marker comments</td><td>Updated by reviewer/marker</td></tr><tr><td><code>superId</code></td><td>User ID of Supervisor</td><td>Set on review submission</td></tr><tr><td><code>reviewedAt</code></td><td>Timestamp of review</td><td>Set on super submission</td></tr><tr><td><code>markerData</code> (JSON)</td><td>Marker comments</td><td>Set by marker</td></tr><tr><td><code>markerId</code></td><td>Marker ID</td><td>Set on moderation</td></tr><tr><td><code>markedAt</code> (optional)</td><td>Timestamp of review</td><td>Set on marker submission</td></tr><tr><td><code>externalData</code> (JSON)</td><td>External examiner comments (optional)</td><td>Set by external</td></tr><tr><td><code>externalId</code></td><td>External ID</td><td>Set on moderation</td></tr><tr><td><code>externalAt</code> (optional)</td><td>Timestamp of external view of record</td><td>Set on external submission</td></tr></tbody></table>
<hr>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="student-workflow">Student Workflow<a href="https://docs.e-rwu.biz/pl/blog/v1.1.0#student-workflow" class="hash-link" aria-label="Bezpośredni link do Student Workflow" title="Bezpośredni link do Student Workflow" translate="no">​</a></h3>
<p><strong>Student selects placement, then task record</strong></p>
<ul>
<li class="">From placements, student selects button (based on module and/or placement) to open a list of tasks to complete in deadline date order. Ordered by deadline date, oldest at bottom.</li>
<li class="">Student selects task (restricted if isLocked &gt; 0 or too far in the future?)</li>
<li class="">Do we want student to see all submission in the past?? Once it is finalised it could be unlocked locally in FileMaker to allow modification - what happens to finalSubmit date?</li>
</ul>
<ol>
<li class="">
<p><strong>Open/Active</strong></p>
<ul>
<li class="">SurveyJS renders initial student questions editable, and others hidden</li>
<li class="">Autosave triggers write to <code>savedData</code> and <code>lastSubmit</code></li>
</ul>
</li>
</ol>
<div class="language-ts codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-ts codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">survey</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">onValueChanged</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">add</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token function" style="color:#d73a49">debounceSave</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">survey</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">data</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">survey</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">onValueChanged</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">add</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">sender</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">currentUser</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">role </span><span class="token operator" style="color:#393A34">===</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"student"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token function" style="color:#d73a49">autosaveStudentAnswers</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">sender</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">data</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><br></span></code></pre></div></div>
<ol start="2">
<li class="">
<p><strong>Finalise Submission</strong></p>
<ul>
<li class="">Sets <code>finalSubmit</code></li>
<li class="">Updates <code>status =&gt; 1</code> (submitted)</li>
<li class="">Evaluates <code>isLate</code></li>
<li class="">Triggers webhook for external audit/export</li>
</ul>
</li>
</ol>
<div class="language-ts codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-ts codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">currentUser</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">role </span><span class="token operator" style="color:#393A34">===</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"student"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">await</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">fetch</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"/api/student-task/finalise"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    method</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"POST"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    body</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token constant" style="color:#36acaa">JSON</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">stringify</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      studentTaskId</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      savedData</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> survey</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">data</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre></div></div>
<hr>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="supervisor--marker-workflow">Supervisor / Marker Workflow<a href="https://docs.e-rwu.biz/pl/blog/v1.1.0#supervisor--marker-workflow" class="hash-link" aria-label="Bezpośredni link do Supervisor / Marker Workflow" title="Bezpośredni link do Supervisor / Marker Workflow" translate="no">​</a></h3>
<ul>
<li class="">SurveyJS renders <strong>student answers read-only</strong></li>
<li class="">Supervisor/marker questions are visible and editable, based on <code>role</code></li>
<li class="">No autosave; manual submission only</li>
</ul>
<div class="language-ts codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-ts codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">currentUser</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">role </span><span class="token operator" style="color:#393A34">===</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"super"</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">||</span><span class="token plain"> currentUser</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">role </span><span class="token operator" style="color:#393A34">===</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"marker"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">await</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">fetch</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"/api/student-task/review"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    method</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"POST"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    body</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token constant" style="color:#36acaa">JSON</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">stringify</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      studentTaskId</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      reviewerData</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> survey</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">data</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      reviewerId</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> currentUser</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">id</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      reviewedAt</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">new</span><span class="token plain"> </span><span class="token class-name">Date</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">toISOString</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre></div></div>
<ul>
<li class=""><code>reviewerData</code> or <code>markerData</code> is stored separately from <code>savedData</code></li>
<li class="">Short questions mean manual submit is sufficient</li>
</ul>
<hr>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="external-examiner-workflow">External Examiner Workflow<a href="https://docs.e-rwu.biz/pl/blog/v1.1.0#external-examiner-workflow" class="hash-link" aria-label="Bezpośredni link do External Examiner Workflow" title="Bezpośredni link do External Examiner Workflow" translate="no">​</a></h3>
<ul>
<li class="">
<p>Receives <strong>concatenated export snapshot</strong> including:</p>
<ul>
<li class="">Student answers from (<code>savedData</code>)</li>
<li class="">Reviewer comments from (<code>reviewerData</code>)</li>
<li class="">Timestamps (<code>lastSubmit</code>, <code>finalSubmit</code>, <code>reviewedAt</code>) ??</li>
<li class="">Late flag (<code>isLate</code>) ??</li>
</ul>
</li>
<li class="">
<p>No SurveyJS rendering required, simpler - could also be a PDF format to download?</p>
</li>
</ul>
<hr>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="role-based-save--submission-rules">Role-Based Save / Submission Rules<a href="https://docs.e-rwu.biz/pl/blog/v1.1.0#role-based-save--submission-rules" class="hash-link" aria-label="Bezpośredni link do Role-Based Save / Submission Rules" title="Bezpośredni link do Role-Based Save / Submission Rules" translate="no">​</a></h3>
<table><thead><tr><th>Role</th><th>Save Target</th><th>Autosave?</th><th>Submission / Finalise</th></tr></thead><tbody><tr><td>Student</td><td><code>savedData</code></td><td>Yes</td><td>Finalise → sets <code>finalSubmit</code>, triggers webhook</td></tr><tr><td>Supervisor</td><td><code>reviewerData</code></td><td>No</td><td>Manual Submit → sets <code>reviewedAt</code></td></tr><tr><td>Marker</td><td><code>markerData</code></td><td>No</td><td>Manual Submit → sets <code>markedAt</code></td></tr><tr><td>External</td><td>N/A (read-only)</td><td>N/A</td><td>N/A</td></tr></tbody></table>
<hr>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="studenttask-lifecycle-diagram-ascii">StudentTask Lifecycle Diagram (ASCII)<a href="https://docs.e-rwu.biz/pl/blog/v1.1.0#studenttask-lifecycle-diagram-ascii" class="hash-link" aria-label="Bezpośredni link do StudentTask Lifecycle Diagram (ASCII)" title="Bezpośredni link do StudentTask Lifecycle Diagram (ASCII)" translate="no">​</a></h3>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">        ┌───────────────────────────────────┐</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        │   Student Placement Tasks         │</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        └───────────────────────────────────┘</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                  │</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                  ▼</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">       ┌─────────────────────┐</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">       │ Open/Active.        │</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">       │ (savedData updated, │</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">       │ lastSubmit)         │</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">       └───────┬─────────────┘</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">               │</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">       Save / Autosave (student only)</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">               │</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">               ▼</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">       ┌─────────────────────┐</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">       │ Finalise Submission │</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">       │ finalSubmit set     │</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">       │ status==1           │</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">       │ isLate evaluated    │</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">       └───────┬─────────────┘</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">               │</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">               ▼</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        ┌───────────────┐</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        │  ++ Webhook   │</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        │ Export Trigger│</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        └───────┬───────┘</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                │</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                ▼</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  ┌────────────────────────┐</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  │ Role-Based Review      │</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  └─────────────┬──────────┘</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                │</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    ┌───────────┴───────────┐</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    ▼                       ▼</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">┌───────────────┐         ┌───────────────┐</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">│ Supervisor    │         │ Marker        │</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">│ Review Task   │         │ Review Task   │</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">│ (reviewerData │         │ (markerData   │</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">│ + reviewedAt) │         │ + markedAt)   │</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">└───────┬───────┘         └───────┬───────┘</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        │                         │</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        └───────► Manual Submit  ◄┘</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                │</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                ▼</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">     ┌─────────────────────┐</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">     │ Final Marking       │</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">     │ Done in FileMaker   │</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">     │ from aggregate +    │</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">     │ isLate flags +      │</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">     │ time records data   │</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">     │ transferred to ARLT │</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">     └─────────────────────┘</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                │</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                ▼</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">     ┌─────────────────────┐</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">     │ External Examiner   │</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">     │ Receives full export│</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">     │ (student answers +  │</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">     │ reviewerData +      │</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">     │ timestamps + isLate)│</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">     └─────────────────────┘</span><br></span></code></pre></div></div>
<hr>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="key-definitions--conclusions">Key Definitions &amp; Conclusions<a href="https://docs.e-rwu.biz/pl/blog/v1.1.0#key-definitions--conclusions" class="hash-link" aria-label="Bezpośredni link do Key Definitions &amp; Conclusions" title="Bezpośredni link do Key Definitions &amp; Conclusions" translate="no">​</a></h3>
<ol>
<li class="">
<p><strong>Student and reviewer/marker answer data stored separately</strong></p>
<ul>
<li class=""><code>savedData</code> → student</li>
<li class=""><code>reviewerData</code> → supervisor</li>
<li class=""><code>markerData</code> → marker/tutor/admin</li>
</ul>
</li>
<li class="">
<p><strong>Autosave limited to student role</strong></p>
<ul>
<li class="">Prevents overwriting by other roles</li>
<li class="">Reduces chance of lost-in-translation transport errors</li>
<li class="">Only needed for student editing mode</li>
</ul>
</li>
<li class="">
<p><strong>Finalise submission / Submit button</strong></p>
<ul>
<li class="">Students → triggers finalise + webhook</li>
<li class="">Supervisor/marker → manual submit of short answers only</li>
</ul>
</li>
<li class="">
<p><strong>Late flag (<code>isLate</code>)</strong></p>
<ul>
<li class="">Derived from <code>finalSubmit</code> vs <code>deadlineDate</code> in FileMaker</li>
<li class="">Students can submit late; flag used for audit/reporting</li>
</ul>
</li>
<li class="">
<p><strong>Workflow metadata tracked via timestamps</strong></p>
<ul>
<li class=""><code>lastSubmit</code> → autosave / last student activity</li>
<li class=""><code>finalSubmit</code> → 'finalised' submission</li>
<li class=""><code>reviewedAt</code> → supervisor/reviewer submission</li>
<li class=""><code>markedAt</code> → marker comments???</li>
</ul>
</li>
<li class="">
<p><strong>External audit/export</strong></p>
<ul>
<li class="">Webhook captures snapshot on finalise, to audit log endpoint</li>
<li class="">Supports external Examiner review without SurveyJS</li>
</ul>
</li>
</ol>
<hr>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="language">Language<a href="https://docs.e-rwu.biz/pl/blog/v1.1.0#language" class="hash-link" aria-label="Bezpośredni link do Language" title="Bezpośredni link do Language" translate="no">​</a></h3>
<p>SurveyJS 'questions' we refer to a 'Task', comprising one or more questions
A collection of Tasks that is against a student, additionally with an academicYear and deadline, is a 'StudentTask'
The StudentTask is considered Open until a second button both saves answer data, and 'finalises' the record by adding a second timestamp value.
The submitted answers for a StudentTask is available to be reviewed, firstly by 'Supervisor', and subsequently by a 'Marker'
If the submission of a Task is after the Deadline, then it is considered 'Late', based on a field which auto-calculates the</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="model-considerations">Model considerations<a href="https://docs.e-rwu.biz/pl/blog/v1.1.0#model-considerations" class="hash-link" aria-label="Bezpośredni link do Model considerations" title="Bezpośredni link do Model considerations" translate="no">​</a></h3>
<p>By storing the JSON for the questions on the StudentTask records, then the data will match the related questions. If there is a need to modify (say a question wording) then that is on the Task record until pulled into the StudentTask record(s). Structural changes (particularly modifiying question numbers) should therefore <strong>ONLY</strong> be done mid-year, for Tasks that are not yet near their deadline. To get the Tasks dynamically runs the risk of an update being prepared for another year then being returned to be answered. This also involves two api calls - one for model and one for data.</p>
<p>If <code>status &gt; 0</code> (finlised), then:</p>
<ul>
<li class="">savedData must never change again</li>
<li class="">*unless* unlocked by an admin (this will be logged) - what happens to deadline, isLate, finalSubmit??</li>
<li class="">Only superData can be added</li>
<li class="">Only markerData can be added</li>
</ul>
<!-- -->
<div class="theme-admonition theme-admonition-tip admonition_xJq3 alert alert--success"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 12 16"><path fill-rule="evenodd" d="M6.5 0C3.48 0 1 2.19 1 5c0 .92.55 2.25 1 3 1.34 2.25 1.78 2.78 2 4v1h5v-1c.22-1.22.66-1.75 2-4 .45-.75 1-2.08 1-3 0-2.81-2.48-5-5.5-5zm3.64 7.48c-.25.44-.47.8-.67 1.11-.86 1.41-1.25 2.06-1.45 3.23-.02.05-.02.11-.02.17H5c0-.06 0-.13-.02-.17-.2-1.17-.59-1.83-1.45-3.23-.2-.31-.42-.67-.67-1.11C2.44 6.78 2 5.65 2 5c0-2.2 2.02-4 4.5-4 1.22 0 2.36.42 3.22 1.19C10.55 2.94 11 3.94 11 5c0 .66-.44 1.78-.86 2.48zM4 14h5c-.23 1.14-1.3 2-2.5 2s-2.27-.86-2.5-2z"></path></svg></span>wskazówka</div><div class="admonitionContent_BuS1"><p>createExternalRecord - present as Markdown?</p><div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">function createExternalRecord(studentTask) {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  let text = `Student Inputs:\n`;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  for (const [q, a] of Object.entries(studentTask.savedData)) {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    text += `  ${q}: ${a}\n`;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  if (studentTask.superData) {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    text += `\nReviewer Comments (by ${studentTask.superId} at ${studentTask.reviewedAt}):\n`;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    for (const [k, v] of Object.entries(studentTask.superData)) {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      text += `  ${k}: ${v}\n`;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  if (studentTask.markerData) {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    text += `\nModerator Comments (by ${studentTask.markerId} at ${studentTask.markeddAt}):\n`;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    for (const [k, v] of Object.entries(studentTask.markerData)) {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      text += `  ${k}: ${v}\n`;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  return text;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">}</span><br></span></code></pre></div></div></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="useful-code-snippets">Useful code snippets<a href="https://docs.e-rwu.biz/pl/blog/v1.1.0#useful-code-snippets" class="hash-link" aria-label="Bezpośredni link do Useful code snippets" title="Bezpośredni link do Useful code snippets" translate="no">​</a></h3>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">// App.tsx</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">&lt;Route path="/tasks/:taskId" element={&lt;TaskSurvey /&gt;} /&gt;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">// TaskSurvey.tsx</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">const navigate = useNavigate();</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">//Express proxy</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">app.post("/submit", async (req, res) =&gt; {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  try {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    const response = await fetch(process.env.ODATA_URL, {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      method: "POST",</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      headers: {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        "Content-Type": "application/json",</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        "Authorization": `Bearer ${process.env.ODATA_TOKEN}`</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      },</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      body: JSON.stringify(req.body)</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    });</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    const data = await response.json();</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    res.json(data);</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  } catch (err) {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    res.status(500).json({ error: "Submission failed" });</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">});app.post("/submit", async (req, res) =&gt; {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  try {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    const response = await fetch(process.env.ODATA_URL, {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      method: "POST",</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      headers: {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        "Content-Type": "application/json",</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        "Authorization": `Bearer ${process.env.ODATA_TOKEN}`</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      },</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      body: JSON.stringify(req.body)</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    });</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    const data = await response.json();</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    res.json(data);</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  } catch (err) {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    res.status(500).json({ error: "Submission failed" });</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">});</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">//pattern</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">import { Model } from "survey-core";</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">import { Survey } from "survey-react-ui";</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">const TaskSurvey = () =&gt; {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  const [model, setModel] = useState&lt;Model&gt;();</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  useEffect(() =&gt; {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    async function loadSurvey() {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      const definition = await fetchSurveyDefinition();</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      const savedData = await fetchSavedAnswers();</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      const survey = new Model(definition);</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      survey.data = savedData;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      survey.onComplete.add(async (sender) =&gt; {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        await submitSurvey(sender.data);</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      });</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      setModel(survey);</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    loadSurvey();</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  }, []);</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  return model ? &lt;Survey model={model} /&gt; : &lt;div&gt;Loading...&lt;/div&gt;;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">};</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">//express</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">import express from "express";</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">import fetch from "node-fetch";</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">import cors from "cors";</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">const app = express();</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">app.use(cors());</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">app.use(express.json());</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">app.post("/submit", async (req, res) =&gt; {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  try {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    const response = await fetch(process.env.ODATA_URL!, {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      method: "POST",</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      headers: {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        "Content-Type": "application/json",</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        "Authorization":</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          "Basic " +</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          Buffer.from(</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            process.env.ODATA_USER + ":" + process.env.ODATA_PASS</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          ).toString("base64"),</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      },</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      body: JSON.stringify(req.body),</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    });</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    const text = await response.text();</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    res.status(response.status).send(text);</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  } catch (err) {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    console.error(err);</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    res.status(500).json({ error: "Submission failed" });</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">});</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">app.listen(3005, () =&gt; {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  console.log("Proxy running on port 3005");</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">});</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">const surveyJson = JSON.parse(data.QuestionsField);</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">const themeJson = JSON.parse(data.ThemeField);</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">const savedData = JSON.parse(data.SavedField ?? "{}");</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">//task</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">import { Model } from "survey-core";</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">import { Survey } from "survey-react-ui";</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">const TaskSurvey = ({ surveyJson, themeJson, savedData }) =&gt; {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  const survey = new Model(surveyJson);</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  survey.data = savedData ?? {};</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  survey.applyTheme(themeJson);</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  survey.onComplete.add(async (sender) =&gt; {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    await submitSurvey(sender.data);</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  });</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  return &lt;Survey model={survey} /&gt;;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">};</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">//at first route load</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">let globalTheme: any = null;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">export async function loadThemeOnce() {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  if (globalTheme) return globalTheme;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  const res = await fetch("/api/theme");</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  const data = await res.json();</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  globalTheme = JSON.parse(data.themeJson);</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  return globalTheme;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">}</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">//inside page</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">const theme = await loadThemeOnce();</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">survey.applyTheme(theme);</span><br></span></code></pre></div></div>
<div class="theme-admonition theme-admonition-info admonition_xJq3 alert alert--info"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 14 16"><path fill-rule="evenodd" d="M7 2.3c3.14 0 5.7 2.56 5.7 5.7s-2.56 5.7-5.7 5.7A5.71 5.71 0 0 1 1.3 8c0-3.14 2.56-5.7 5.7-5.7zM7 1C3.14 1 0 4.14 0 8s3.14 7 7 7 7-3.14 7-7-3.14-7-7-7zm1 3H6v5h2V4zm0 6H6v2h2v-2z"></path></svg></span>informacja</div><div class="admonitionContent_BuS1"><div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">Student clicks task</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        ↓</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">React route /task/:taskId</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        ↓</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">Backend fetches StudentTasks record</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        ↓</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">Returns:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    surveyJson</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    savedData</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        ↓</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">Frontend:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    Parse JSON</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    Load global theme</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    Create Survey Model</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    Inject saved data</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    Render</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        ↓</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">On complete:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    POST to proxy</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        ↓</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">Proxy writes to OData (Basic auth)</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">//additonal button</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">survey.addNavigationItem({</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  id: "finalise",</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  title: "Finalise Submission",</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  action: async () =&gt; {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    await finaliseSubmission(survey.data);</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  },</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  css: "sd-btn--action"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">});</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">POST /api/student-task/save</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">POST /api/student-task/finalise</span><br></span></code></pre></div></div></div></div>]]></content>
        <author>
            <name>John Renfrew</name>
            <uri>https://linkedin.com/in//john-renfrew-81780614</uri>
        </author>
        <category label="Test" term="Test"/>
        <category label="Revision" term="Revision"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[v1.0.10]]></title>
        <id>https://docs.e-rwu.biz/pl/blog/v1.0.10</id>
        <link href="https://docs.e-rwu.biz/pl/blog/v1.0.10"/>
        <updated>2026-02-27T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[Version 1.0.10 internal test release]]></summary>
        <content type="html"><![CDATA[<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="version-1010-internal-test-release">Version 1.0.10 internal test release<a href="https://docs.e-rwu.biz/pl/blog/v1.0.10#version-1010-internal-test-release" class="hash-link" aria-label="Bezpośredni link do Version 1.0.10 internal test release" title="Bezpośredni link do Version 1.0.10 internal test release" translate="no">​</a></h3>
<ul>
<li class="">surveyjs theme editor prep work</li>
</ul>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="theme-editor">Theme editor<a href="https://docs.e-rwu.biz/pl/blog/v1.0.10#theme-editor" class="hash-link" aria-label="Bezpośredni link do Theme editor" title="Bezpośredni link do Theme editor" translate="no">​</a></h3>
<p><strong>SurveyJS</strong> has a Theme Editor which provides a method to preview a modified theme for the/ elements. This is saved as a JSON definition file, which can then be injected into the code for rendering the page, along with the definition JSON.<br>
It does mean (although I am not going to recommend this), that each task, or similar group of tasks, could have their own theme. The following samples show tasks with the default theme over-ridden.<br></p>
<ul>
<li class="">Sample 1 is for student use, has three buttons for different actions, different colours and hover state</li>
<li class="">Sample 2 is a simple confirmation task for supervisors, with Czech translation as the 'description'</li>
<li class="">Sample 3 has a link to a downloadable file, with an upload widget, to demonstrate the idea of offline answers</li>
</ul>
<div class="theme-admonition theme-admonition-info admonition_xJq3 alert alert--info"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 14 16"><path fill-rule="evenodd" d="M7 2.3c3.14 0 5.7 2.56 5.7 5.7s-2.56 5.7-5.7 5.7A5.71 5.71 0 0 1 1.3 8c0-3.14 2.56-5.7 5.7-5.7zM7 1C3.14 1 0 4.14 0 8s3.14 7 7 7 7-3.14 7-7-3.14-7-7-7zm1 3H6v5h2V4zm0 6H6v2h2v-2z"></path></svg></span>informacja</div><div class="admonitionContent_BuS1"><a href="https://docs.e-rwu.biz/samples/one/" target="_blank" rel="noopener noreferrer">Sample 1</a><br><a href="https://docs.e-rwu.biz/samples/two/" target="_blank" rel="noopener noreferrer">Sample 2</a><br><a href="https://docs.e-rwu.biz/samples/three/" target="_blank" rel="noopener noreferrer">Sample 3</a></div></div>]]></content>
        <author>
            <name>John Renfrew</name>
            <uri>https://linkedin.com/in//john-renfrew-81780614</uri>
        </author>
        <category label="Test" term="Test"/>
        <category label="Revision" term="Revision"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[v1.0.9]]></title>
        <id>https://docs.e-rwu.biz/pl/blog/v1.0.9</id>
        <link href="https://docs.e-rwu.biz/pl/blog/v1.0.9"/>
        <updated>2026-02-27T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[Version 1.0.9 internal test release]]></summary>
        <content type="html"><![CDATA[<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="version-109-internal-test-release">Version 1.0.9 internal test release<a href="https://docs.e-rwu.biz/pl/blog/v1.0.9#version-109-internal-test-release" class="hash-link" aria-label="Bezpośredni link do Version 1.0.9 internal test release" title="Bezpośredni link do Version 1.0.9 internal test release" translate="no">​</a></h3>
<ul>
<li class="">Discussions about requirent for other core translations to this site</li>
<li class="">Futher definition of tasks off the student record (StudentTasks) and how they map to actual placements</li>
<li class="">Consideration of other types of input, or methods of interaction. Example of a downloadable template which can be filled, uploaded with the text extracted and stored.</li>
<li class="">Investigation of Echo360, which has student accounts, for the purpose of uploading video files (within limits), which returns a link to be added to the task record.</li>
</ul>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="use-cases">Use cases<a href="https://docs.e-rwu.biz/pl/blog/v1.0.9#use-cases" class="hash-link" aria-label="Bezpośredni link do Use cases" title="Bezpośredni link do Use cases" translate="no">​</a></h4>
<p>A student may be on a single placement a year for one module only. This is the simplest case to programe and manage. <em>[plain]</em><br>
They may have more than one placment in a year, each attached to one module. <em>[multi]</em><br>
They may have a placmement that is attached to two modules, and so the task need to be assigned to the module and the placement. <em>[split]</em><br>
They may do an additional (short) placment for one module. <em>[extra]</em><br>
THe case may be that there are combinations of number of placements and whether one or more has more than one mudule attached. <em>[complex]</em><br>
They may change placment during the year which then remains linked to the same module(s) as the one it replaces. <em>[replace]</em><br>
There may be tasks which are floating to the module, and so may be completed on any placement associated with that module. <em>[float]</em><br>
There may be tasks which are floating to the placement, and also floating to either module. <em>[free]</em><br>
This implies that until a StudentTask has been assigned a module, it is available to all, or all in the same placement, but then after that is becomes fixed for the purpose of marking.</p>
<p>The assemblage of StudentTask records allow for those to be marked and entered into the related ARLT record, which imolies that whether this was completed in a single placement or multiple, these records can be filtered down to just those linked to a specific module.</p>
<p>Because all supervisors and some markers will be using the web front-end, it is desirable to not have to download local copies of files to see their content, and that linked video should be shown in new browser window.</p>
<!-- -->
<img src="https://docs.e-rwu.biz/pl/assets/images/types_00-98bdc035ce78dd27322f405b0903af32.png" alt="portfolio" style="width:300px">]]></content>
        <author>
            <name>John Renfrew</name>
            <uri>https://linkedin.com/in//john-renfrew-81780614</uri>
        </author>
        <category label="Test" term="Test"/>
        <category label="Revision" term="Revision"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[v1.0.8]]></title>
        <id>https://docs.e-rwu.biz/pl/blog/v1.0.8</id>
        <link href="https://docs.e-rwu.biz/pl/blog/v1.0.8"/>
        <updated>2026-02-21T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[Version 1.0.8 internal test release]]></summary>
        <content type="html"><![CDATA[<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="version-108-internal-test-release">Version 1.0.8 internal test release<a href="https://docs.e-rwu.biz/pl/blog/v1.0.8#version-108-internal-test-release" class="hash-link" aria-label="Bezpośredni link do Version 1.0.8 internal test release" title="Bezpośredni link do Version 1.0.8 internal test release" translate="no">​</a></h3>
<ul>
<li class="">Decision - this is all going to be available at <strong>portal.moorlands.ac.uk</strong> and <strong>docs.moorlands.ac.uk</strong></li>
<li class="">After initial demos, we are adding translation to this site, so that MCEE students and supervisors can have documentation in Czech (Čeština)</li>
<li class="">Base structure creaated for forms, discussion about language below</li>
</ul>
<p>Alternate language content may be reached from the drop-down top right of the site, with Cestina being the only choice. Notes about this are added to appropriate pages. Translation is done using <a href="https://aws.amazon.com/translate/" target="_blank" rel="noopener noreferrer" class="">Amazon Translate</a> from AWS, and we need a mechanism for proof-reading and corrections where appropriate.</p>
<p>The two things a student has to complete during a placement are records of time with activity, and a series of questions that require a typed answer. The first of these will be referred to as <em><strong>Time Records</strong></em>. The second will be called <em><strong>Task Entries</strong></em>. The entry may be a text input, but also any other appropriate control (like a tickbox or radio set, or scale or priority ordering) There may be requirement for additional doument uploads, and the precise nature of these is currently being discussed.</p>
<p>The 'task entries' are a collection of questions created in surveyjs with the describing JSON being saved on a FileMaker record (through the _portfolio file). Each question may have read-only and visibility criteria added, based on data that is added to the student's saved data, modifying the form behaviour. At the 'top' are questions visible and editable by students. Next are questions only visible to supervisors, while the student answers become read-only. After that are questions for markers with all the above noiw read-only. And finally external examiners may have their own question to confirm they have reviewed the record, with all other answers read-only.</p>
<p>The questions are then gathered in a set or bundle that may be common to one or more module placements. They are marked with year(s) for which they are considered active. These template-bundles are then used to create a module-specific template for a specific academic year. The module-specific template may consist of one or more template-bundles and also individual tasks. At this point each of the tasks now in the module-specific template is assigned group visibility (so a task may only be visible to a supervisor or marker, in addition to the behaviour within the task outlined above). Dealine dates are added for each of the (currently 3) centres.</p>
<p>A student placement will be linked to one of these module/year-specific templates, and a scripted process will add an individualised copy of each of the template records <strong>(StudentTasks)</strong>, using the appropriate deadline data based on the Study Centre in Sopley Central and the JSON object which defines the specific questions in the task. These will be first ordered by deadline date before creation, so that the filemaker view will naturally sort to this order. These are the individual endpoints where student/ supervisor/ marker responses will be held, and from where they can be pulled to re-hydrate any tasks.</p>
<p>The relationship graph to enable this currently looks like this in _dev</p>
<!-- -->
<img src="https://docs.e-rwu.biz/pl/assets/images/RG_01-a82df871054310171a897a173b86514a.png" alt="portfolio" style="width:800px">]]></content>
        <author>
            <name>John Renfrew</name>
            <uri>https://linkedin.com/in//john-renfrew-81780614</uri>
        </author>
        <category label="Test" term="Test"/>
        <category label="Revision" term="Revision"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[v1.0.7]]></title>
        <id>https://docs.e-rwu.biz/pl/blog/v1.0.7</id>
        <link href="https://docs.e-rwu.biz/pl/blog/v1.0.7"/>
        <updated>2026-02-19T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[Version 1.0.7 internal test release]]></summary>
        <content type="html"><![CDATA[<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="version-107-internal-test-release">Version 1.0.7 internal test release<a href="https://docs.e-rwu.biz/pl/blog/v1.0.7#version-107-internal-test-release" class="hash-link" aria-label="Bezpośredni link do Version 1.0.7 internal test release" title="Bezpośredni link do Version 1.0.7 internal test release" translate="no">​</a></h3>
<ul>
<li class="">Further work to define the path from task entries to Placements and back out to saved records</li>
<li class="">Outline in FileMaker to model the required relationships</li>
<li class="">Consideration to packege tasks into templates, and how they get added to a placement</li>
<li class="">Additional possibility to add a single task to a placement</li>
<li class="">Creation of Studio app to be a simple mobile time entry device</li>
<li class="">Exploratory work with surveyjs to be the enab;ing tech for the task entries</li>
</ul>
<p>The Placement for each student needs a set of records that to are to be filled over time. Each of these need to store the 'submission' into a record which contains a deadline date, and an indicator of late submission and a locked status. The intent is that the data is stored as JSON, so that a part-filled form can be recreated (to have additional comments added)</p>
<p>This will be well served by defining how to create a collection of tasks into a 'bundle' (plan in OneFile language). These would be stored on records with an acYear indicator, which will allow for then to be used as a starting point for next year. They are also linked to modules, either as a full or partial tenplate. Given that the number of placement enable modules is currently 10, this is envisaged to require moderate effort. Because these bundles are leading to a 'mark' that ends up on an ARLT record, there is need to ensure that the order of record creation is important.</p>
<p>Once the bundle is fixed and dealine dates applied, given that there are (at the moment) 3 different centrea, then the lowest impact way is to enter each of the dates onto the bundle for this current year. Students who are taking the module/placement can then be given their own copy of the bundle of tasks in the template.</p>
<p>There are open questions about where a placement consists of more than one module, the neccessary actions in the case that a placment is changed part-way through the year, and the potentiasl requirement for individual tasks to be added to a placment.</p>
<p>A <strong>Studio</strong> form was built as a starter for simple date/time entry and can be found <a href="https://platform.claris.com/form/3328/17#s=bXN0XzM5dFdya0RKWHBwdWF2OEtPdFJMWENGQmh2d2dGMWlZOjY0ZGEzYzM0N2M0ZDg5N2Y5ZmI5YTcxYTk5N2U3MzIxMjA2MTZhNTRhNGQzMjA0MjdlYjc2YWRjOTMxOWNhMzc=" target="_blank" rel="noopener noreferrer" class=""><strong>here</strong></a></p>
<p>The <strong>surveyjs</strong> work has been to explore the mode advanced capabilities, like hidden fileds and ensuring they are passed through to the saved data, locking or showing fields when (for example) a supervisor ID is added to the record. The outlined behaviour is that if the superID is present then the student contributions become locked, and in the absence of a superID their comments are hidden (what about later when you want the student to see what was written?). Initial tests have data flowing into FileMaker records, which is the desired outcome!</p>
<p>There is a concept for both time and task records, to include a status flag, enabling them to act as 'locked' in the UI, but because these are available as FileMaker records, then overriding the behaviour is simole to achieve.</p>]]></content>
        <author>
            <name>John Renfrew</name>
            <uri>https://linkedin.com/in//john-renfrew-81780614</uri>
        </author>
        <category label="Test" term="Test"/>
        <category label="Revision" term="Revision"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[v1.0.6]]></title>
        <id>https://docs.e-rwu.biz/pl/blog/v1.0.6</id>
        <link href="https://docs.e-rwu.biz/pl/blog/v1.0.6"/>
        <updated>2026-02-18T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[Version 1.0.6 internal test release]]></summary>
        <content type="html"><![CDATA[<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="version-106-internal-test-release">Version 1.0.6 internal test release<a href="https://docs.e-rwu.biz/pl/blog/v1.0.6#version-106-internal-test-release" class="hash-link" aria-label="Bezpośredni link do Version 1.0.6 internal test release" title="Bezpośredni link do Version 1.0.6 internal test release" translate="no">​</a></h3>
<ul>
<li class="">Initial ideas for display of student list for external examiners (compact view)</li>
<li class="">Development on time entries for student portfolio, buttonon placement object</li>
<li class="">Connection of portfolio models to placement records with eSQL query</li>
</ul>
<p>Created a new display component to present compact or expanded view of student placments, based on query to return relevant set of placement records. For external examiners this is limited to module, student name, organisation</p>
<p>Added initial idea for time-record component linked to placement records. Function is to display all submitted time records, in reverse order, to indicate which are flagged as locked, and so unable to be ediited ( this could be modified by admin in FMP ), allow for the creation and saving of a new record, or if icon selected to push detail into entry section for updating.</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">Structure =&gt; {"date","time","type","description",ROWID}</span><br></span></code></pre></div></div>
<!-- -->
<img src="https://docs.e-rwu.biz/pl/assets/images/placement_02-2e8e3c48f6a814b1b8b65cd617bedfce.png" alt="portfolio" style="width:800px">
<p>Student &gt; course &gt; level (acYear) &gt; module choices &gt; module &gt; ARLT marking records<br>
Module &gt; placement &gt; tasks &gt; marking &gt; ARLT</p>
<p>A <strong>placement</strong> record is a join between student, acYear, module ++ assignment. It links via record keys to organisations and people. Further elements hang off each one - time/diary records and tasks.</p>]]></content>
        <author>
            <name>John Renfrew</name>
            <uri>https://linkedin.com/in//john-renfrew-81780614</uri>
        </author>
        <category label="Test" term="Test"/>
        <category label="Revision" term="Revision"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[v1.0.5]]></title>
        <id>https://docs.e-rwu.biz/pl/blog/v1.0.5</id>
        <link href="https://docs.e-rwu.biz/pl/blog/v1.0.5"/>
        <updated>2026-02-16T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[Version 1.0.5 internal test release]]></summary>
        <content type="html"><![CDATA[<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="version-105-internal-test-release">Version 1.0.5 internal test release<a href="https://docs.e-rwu.biz/pl/blog/v1.0.5#version-105-internal-test-release" class="hash-link" aria-label="Bezpośredni link do Version 1.0.5 internal test release" title="Bezpośredni link do Version 1.0.5 internal test release" translate="no">​</a></h3>
<ul>
<li class="">Code refactoring to cache metadata IDs server-side</li>
<li class="">Investigations into speed of invoices retrieval</li>
<li class="">Moved processing of data containing pre-composed JSON to server-side</li>
<li class="">Core novigation structure becoming defined</li>
</ul>
<p>Cache of metatdata can improve performance whenever a page or route needs to use or display items we are storing - like an ID. It is stored in the Clerk API and so request use involves a query, which can be sped up with the used of cached data. This data changes very rarely and so this pattern is used to hide the IDs away, while making them available for server-side routes which are using them as filters or keys in OData queries.</p>
<p>To allow for this to be build out from the Student Portal, the following routes enable that. /login =&gt; with a student email =&gt; dashboard, with a staff email =&gt; staff-dashboard. The student version has a link from there into the Digifolio parent.</p>
<p>If a user logs in with an email associated with a superId then they arrive at portfolio-super, with an email associated with a markerId then portfolio-marker, and if external, then portfolio-external. These are siloed from each other, and in the event that there is a staff portal, then there would be a button to enter the digifolio similar to students.</p>
<p>Students will see a picker for the placements they have active during the year, with action buttons for further sections to manage and enter time-based events, and another to enter task data.</p>
<p>Supervisors would see the students they are supervising in the current year, and on selection of the student the detail as above with additional fields for their commments on the student responses to the task questions.</p>
<p>Markers would see a longer version of the list, based on being assigned as marker for a set or module, and other controlsa vailable as above. Markers would be able to add adidtional comments after the supervisor. This may never happen if it is detirmined that all markers will be accessing the data from FileMaker.</p>
<p>External would see a pre-filtered list simialar to above of the random set assigned to them.</p>]]></content>
        <author>
            <name>John Renfrew</name>
            <uri>https://linkedin.com/in//john-renfrew-81780614</uri>
        </author>
        <category label="Test" term="Test"/>
        <category label="Revision" term="Revision"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[v1.0.4]]></title>
        <id>https://docs.e-rwu.biz/pl/blog/v1.0.4</id>
        <link href="https://docs.e-rwu.biz/pl/blog/v1.0.4"/>
        <updated>2026-02-14T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[Version 1.0.4 internal test release]]></summary>
        <content type="html"><![CDATA[<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="version-104-internal-test-release">Version 1.0.4 internal test release<a href="https://docs.e-rwu.biz/pl/blog/v1.0.4#version-104-internal-test-release" class="hash-link" aria-label="Bezpośredni link do Version 1.0.4 internal test release" title="Bezpośredni link do Version 1.0.4 internal test release" translate="no">​</a></h3>
<ul>
<li class="">Schema for metadata started</li>
<li class="">Preparation of supervisor dashboard page</li>
<li class="">Removal of unused and exerimental code to simplify project directory</li>
<li class="">Error page improved and added Pushover notifications</li>
<li class="">Experiments with cache for privateMetadata Id values</li>
</ul>
<p>Changed the components on the trusted contacts and it looks and feels a whole lot better. Now it is using <strong>shadcn</strong> there is some control over colours and styles, but some things just do not work well as <strong>Tailwind</strong> seems to take over, and even though shadcn uses Tailwind colours, not all of them will render. I find that white buttons with white text on a white background is probably too minimalist.</p>
<!-- -->
<img src="https://docs.e-rwu.biz/pl/assets/images/placement_01-05342eb3586be40b79a4ecf8e894b4b3.png" alt="portfolio" style="width:800px">
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="wip">WIP<a href="https://docs.e-rwu.biz/pl/blog/v1.0.4#wip" class="hash-link" aria-label="Bezpośredni link do WIP" title="Bezpośredni link do WIP" translate="no">​</a></h4>
<p>Cache works but is too hard to ensure that it is cleared beween user sessions, not normally a problem for a single user, but makes testing difficult if you change to a staff login, and the cache still thinks you are a student. Potential visibility of values in console logs so shelved until a more robust and secure solution is found. It does make a difference to the overall speed, as it reduces the need to call the Clerk object, which is in itself, defensive coding so that you can not 'jump' to any route endpoint without being logged in.</p>
<div class="theme-admonition theme-admonition-info admonition_xJq3 alert alert--info"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 14 16"><path fill-rule="evenodd" d="M7 2.3c3.14 0 5.7 2.56 5.7 5.7s-2.56 5.7-5.7 5.7A5.71 5.71 0 0 1 1.3 8c0-3.14 2.56-5.7 5.7-5.7zM7 1C3.14 1 0 4.14 0 8s3.14 7 7 7 7-3.14 7-7-3.14-7-7-7zm1 3H6v5h2V4zm0 6H6v2h2v-2z"></path></svg></span>informacja</div><div class="admonitionContent_BuS1"><p><strong>public metadata</strong> - front and back end</p><div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">name: string</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">surname: string</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">fullName: string</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">userType: string</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">isAdmin: int</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">last_updated: timestamp</span><br></span></code></pre></div></div></div></div>
<div class="theme-admonition theme-admonition-info admonition_xJq3 alert alert--info"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 14 16"><path fill-rule="evenodd" d="M7 2.3c3.14 0 5.7 2.56 5.7 5.7s-2.56 5.7-5.7 5.7A5.71 5.71 0 0 1 1.3 8c0-3.14 2.56-5.7 5.7-5.7zM7 1C3.14 1 0 4.14 0 8s3.14 7 7 7 7-3.14 7-7-3.14-7-7-7zm1 3H6v5h2V4zm0 6H6v2h2v-2z"></path></svg></span>informacja</div><div class="admonitionContent_BuS1"><p><strong>private metadata</strong> back-end only, call /api/current-user</p><div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">uniqueId: string uid</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">studentId: int</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">staffId: int</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">superId: int</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">moodleId: int</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">individualId: int</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">externalId: int</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">email: string</span><br></span></code></pre></div></div></div></div>]]></content>
        <author>
            <name>John Renfrew</name>
            <uri>https://linkedin.com/in//john-renfrew-81780614</uri>
        </author>
        <category label="Test" term="Test"/>
        <category label="Revision" term="Revision"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[v1.0.3]]></title>
        <id>https://docs.e-rwu.biz/pl/blog/v1.0.3</id>
        <link href="https://docs.e-rwu.biz/pl/blog/v1.0.3"/>
        <updated>2026-02-13T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[Version 1.0.3 internal test release]]></summary>
        <content type="html"><![CDATA[<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="version-103-internal-test-release">Version 1.0.3 internal test release<a href="https://docs.e-rwu.biz/pl/blog/v1.0.3#version-103-internal-test-release" class="hash-link" aria-label="Bezpośredni link do Version 1.0.3 internal test release" title="Bezpośredni link do Version 1.0.3 internal test release" translate="no">​</a></h3>
<ul>
<li class="">First steps to collecting and displaying Placement data</li>
<li class="">JSON is pre-composed in the PLC table to simplify the fetch</li>
</ul>
<p>The information retrieved is based on a year (where there may be more than one placement per year) so the years are displayed acrosst the top, where you can select the data for that year</p>
<!-- -->
<img src="https://docs.e-rwu.biz/pl/assets/images/placement_00-f0dd273be7cb25917ffca5a576c87b9a.png" alt="portfolio" style="width:800px">
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="wip">WIP<a href="https://docs.e-rwu.biz/pl/blog/v1.0.3#wip" class="hash-link" aria-label="Bezpośredni link do WIP" title="Bezpośredni link do WIP" translate="no">​</a></h4>
<p>Select the <strong>Tasks</strong> button to see a list of deadline dates relating to tasks - from the start of the placement to (n) days in the future. There are indicators showing if the task is editable or not. If it editable (or empty) then selecting the item opens a card where the specific task inputs are presented.</p>
<p>Select the <strong>Time Records</strong> button to see a list of the time records that have been entered, showing date, hours recorded and the first nnn characters of the description so it fits in a reduced one-line space. There is a button to create a new record, which opens a card element to add date, time, description and includes a drop-down for the type (based on the Course requirements)</p>
<p>The project includes <strong><a href="https://ui.shadcn.com/docs/installation" target="_blank" rel="noopener noreferrer" class="">shadcn UI</a></strong> components, initial work to move some elements to their shadcn alternatives, to give a more consistent feel between pages.</p>
<p>Clerk metadata has been refactored so that all IDs are inside privateMetadata, but a single re-usable route can be used server-side to get any or all of the IDs then cache them, so there will be no need for a route to just get student-id or staff-id. Other code refactoring has simplified the number or round trips just to get an ID.</p>]]></content>
        <author>
            <name>John Renfrew</name>
            <uri>https://linkedin.com/in//john-renfrew-81780614</uri>
        </author>
        <category label="Test" term="Test"/>
        <category label="Revision" term="Revision"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[v1.0.2]]></title>
        <id>https://docs.e-rwu.biz/pl/blog/v1.0.2</id>
        <link href="https://docs.e-rwu.biz/pl/blog/v1.0.2"/>
        <updated>2026-02-10T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[Version 1.0.2 internal test release]]></summary>
        <content type="html"><![CDATA[<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="version-102-internal-test-release">Version 1.0.2 internal test release<a href="https://docs.e-rwu.biz/pl/blog/v1.0.2#version-102-internal-test-release" class="hash-link" aria-label="Bezpośredni link do Version 1.0.2 internal test release" title="Bezpośredni link do Version 1.0.2 internal test release" translate="no">​</a></h3>
<ul>
<li class="">Added staff login and portal page</li>
<li class="">Preparation for supervisor ID and portal homepage</li>
<li class="">Code refactoring to ensure navigation directly to any page redirect to login if there is no current session</li>
<li class="">Added pages for Trusted Contacts (different for staff and students)</li>
<li class="">Added links to JotForm collection pages for quick access to forms</li>
</ul>
<p>This publicly accessible test version is hosted on a free instance on Oracle. Work to understand upload, installation and SSL and Nginx configuration.</p>
<p><strong>Trusted Contacts</strong>, for student gets information from NXT table and for staff from STF table. Students have one or two, and staff only 1 with no address data. Provision of edit card below data to modify either of the record values - submission to webhook (<em>altdb ottoInbox</em>) to enable review of information sent, to avoid pranks and bad days.</p>
<p><strong>Staff Portal</strong>, placeholder layout with buttons to JotForms and Trusted Contacts.</p>
<p><strong>Marking</strong>, get data from shadow file as the final field in Sopley Central is a convoluted calculation with the risk of crashing the OData process.</p>
<p>Added the route into empty placeholder files so we can check it is navigating correctly.</p>
<p>Test users, added sam.clement, 2024999 (test user)</p>
<div class="theme-admonition theme-admonition-info admonition_xJq3 alert alert--info"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 14 16"><path fill-rule="evenodd" d="M7 2.3c3.14 0 5.7 2.56 5.7 5.7s-2.56 5.7-5.7 5.7A5.71 5.71 0 0 1 1.3 8c0-3.14 2.56-5.7 5.7-5.7zM7 1C3.14 1 0 4.14 0 8s3.14 7 7 7 7-3.14 7-7-3.14-7-7-7zm1 3H6v5h2V4zm0 6H6v2h2v-2z"></path></svg></span>informacja</div><div class="admonitionContent_BuS1"><p>The first time a user logs in from a new device with Clerk, they receive a confirmation email</p><p>All links in the login component to use social, forget password,signup are turned OFF</p></div></div>]]></content>
        <author>
            <name>John Renfrew</name>
            <uri>https://linkedin.com/in//john-renfrew-81780614</uri>
        </author>
        <category label="Test" term="Test"/>
        <category label="Revision" term="Revision"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[v1.0.1]]></title>
        <id>https://docs.e-rwu.biz/pl/blog/v1.0.1</id>
        <link href="https://docs.e-rwu.biz/pl/blog/v1.0.1"/>
        <updated>2026-01-03T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[Version 1.0.1 initial private release]]></summary>
        <content type="html"><![CDATA[<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="version-101-initial-private-release">Version 1.0.1 initial private release<a href="https://docs.e-rwu.biz/pl/blog/v1.0.1#version-101-initial-private-release" class="hash-link" aria-label="Bezpośredni link do Version 1.0.1 initial private release" title="Bezpośredni link do Version 1.0.1 initial private release" translate="no">​</a></h3>
<ul>
<li class="">Initial work to verify Clerk logins and metadata</li>
<li class="">Simple student portal with button to navigate to other pages</li>
<li class="">Investigations into OData connections to pull data into pages</li>
</ul>
<p><strong>Meals</strong>, retrieves data for last week, this week, and any in the future that have been submitted. This is counted from the starting Monday of today, and is displayed as a table</p>
<p><strong>Invoices</strong>, retrieves data from _finance for invoices related by uniqueID. Invoice Number, Total, Due Date, Status are displayed as a table</p>
<p><strong>Moodle Tasks</strong>, retrieves data (not all students have data, this is for Online and remote), display has summary ring/donut charts, with drilldown when selected</p>
<p>Placeholders for Attendance, DigiFolio, Marking</p>
<p>Test users 2025001, 2025013, matt - login data comes from altdb quicker file</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="admonitions-in-markdown">Admonitions in Markdown<a href="https://docs.e-rwu.biz/pl/blog/v1.0.1#admonitions-in-markdown" class="hash-link" aria-label="Bezpośredni link do Admonitions in Markdown" title="Bezpośredni link do Admonitions in Markdown" translate="no">​</a></h3>
<div class="theme-admonition theme-admonition-note admonition_xJq3 alert alert--secondary"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 14 16"><path fill-rule="evenodd" d="M6.3 5.69a.942.942 0 0 1-.28-.7c0-.28.09-.52.28-.7.19-.18.42-.28.7-.28.28 0 .52.09.7.28.18.19.28.42.28.7 0 .28-.09.52-.28.7a1 1 0 0 1-.7.3c-.28 0-.52-.11-.7-.3zM8 7.99c-.02-.25-.11-.48-.31-.69-.2-.19-.42-.3-.69-.31H6c-.27.02-.48.13-.69.31-.2.2-.3.44-.31.69h1v3c.02.27.11.5.31.69.2.2.42.31.69.31h1c.27 0 .48-.11.69-.31.2-.19.3-.42.31-.69H8V7.98v.01zM7 2.3c-3.14 0-5.7 2.54-5.7 5.68 0 3.14 2.56 5.7 5.7 5.7s5.7-2.55 5.7-5.7c0-3.15-2.56-5.69-5.7-5.69v.01zM7 .98c3.86 0 7 3.14 7 7s-3.14 7-7 7-7-3.12-7-7 3.14-7 7-7z"></path></svg></span>notatka</div><div class="admonitionContent_BuS1"><p>Some <strong>content</strong> with <em>Markdown</em> <code>syntax</code>. Check <a href="https://docs.e-rwu.biz/pl/blog/v1.0.1#" class="">this <code>api</code></a>.</p></div></div>
<div class="theme-admonition theme-admonition-tip admonition_xJq3 alert alert--success"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 12 16"><path fill-rule="evenodd" d="M6.5 0C3.48 0 1 2.19 1 5c0 .92.55 2.25 1 3 1.34 2.25 1.78 2.78 2 4v1h5v-1c.22-1.22.66-1.75 2-4 .45-.75 1-2.08 1-3 0-2.81-2.48-5-5.5-5zm3.64 7.48c-.25.44-.47.8-.67 1.11-.86 1.41-1.25 2.06-1.45 3.23-.02.05-.02.11-.02.17H5c0-.06 0-.13-.02-.17-.2-1.17-.59-1.83-1.45-3.23-.2-.31-.42-.67-.67-1.11C2.44 6.78 2 5.65 2 5c0-2.2 2.02-4 4.5-4 1.22 0 2.36.42 3.22 1.19C10.55 2.94 11 3.94 11 5c0 .66-.44 1.78-.86 2.48zM4 14h5c-.23 1.14-1.3 2-2.5 2s-2.27-.86-2.5-2z"></path></svg></span>wskazówka</div><div class="admonitionContent_BuS1"><p>Some <strong>content</strong> with <em>Markdown</em> <code>syntax</code>. Check <a href="https://docs.e-rwu.biz/pl/blog/v1.0.1#" class="">this <code>api</code></a>.</p></div></div>
<div class="theme-admonition theme-admonition-info admonition_xJq3 alert alert--info"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 14 16"><path fill-rule="evenodd" d="M7 2.3c3.14 0 5.7 2.56 5.7 5.7s-2.56 5.7-5.7 5.7A5.71 5.71 0 0 1 1.3 8c0-3.14 2.56-5.7 5.7-5.7zM7 1C3.14 1 0 4.14 0 8s3.14 7 7 7 7-3.14 7-7-3.14-7-7-7zm1 3H6v5h2V4zm0 6H6v2h2v-2z"></path></svg></span>informacja</div><div class="admonitionContent_BuS1"><p>Some <strong>content</strong> with <em>Markdown</em> <code>syntax</code>. Check <a href="https://docs.e-rwu.biz/pl/blog/v1.0.1#" class="">this <code>api</code></a>.</p></div></div>
<div class="theme-admonition theme-admonition-warning admonition_xJq3 alert alert--warning"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 16 16"><path fill-rule="evenodd" d="M8.893 1.5c-.183-.31-.52-.5-.887-.5s-.703.19-.886.5L.138 13.499a.98.98 0 0 0 0 1.001c.193.31.53.501.886.501h13.964c.367 0 .704-.19.877-.5a1.03 1.03 0 0 0 .01-1.002L8.893 1.5zm.133 11.497H6.987v-2.003h2.039v2.003zm0-3.004H6.987V5.987h2.039v4.006z"></path></svg></span>ostrzeżenie</div><div class="admonitionContent_BuS1"><p>Some <strong>content</strong> with <em>Markdown</em> <code>syntax</code>. Check <a href="https://docs.e-rwu.biz/pl/blog/v1.0.1#" class="">this <code>api</code></a>.</p></div></div>
<div class="theme-admonition theme-admonition-danger admonition_xJq3 alert alert--danger"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 12 16"><path fill-rule="evenodd" d="M5.05.31c.81 2.17.41 3.38-.52 4.31C3.55 5.67 1.98 6.45.9 7.98c-1.45 2.05-1.7 6.53 3.53 7.7-2.2-1.16-2.67-4.52-.3-6.61-.61 2.03.53 3.33 1.94 2.86 1.39-.47 2.3.53 2.27 1.67-.02.78-.31 1.44-1.13 1.81 3.42-.59 4.78-3.42 4.78-5.56 0-2.84-2.53-3.22-1.25-5.61-1.52.13-2.03 1.13-1.89 2.75.09 1.08-1.02 1.8-1.86 1.33-.67-.41-.66-1.19-.06-1.78C8.18 5.31 8.68 2.45 5.05.32L5.03.3l.02.01z"></path></svg></span>ważne</div><div class="admonitionContent_BuS1"><p>Some <strong>content</strong> with <em>Markdown</em> <code>syntax</code>. Check <a href="https://docs.e-rwu.biz/pl/blog/v1.0.1#" class="">this <code>api</code></a>.</p></div></div>]]></content>
        <author>
            <name>John Renfrew</name>
            <uri>https://linkedin.com/in//john-renfrew-81780614</uri>
        </author>
        <category label="Test" term="Test"/>
        <category label="Revision" term="Revision"/>
    </entry>
</feed>