@@ -251,13 +251,115 @@ export class AppHandler {
251251 }
252252 )
253253 }
254+ if ( data . ipynbs ) {
255+ if (
256+ ! data . ipynbs . id ||
257+ typeof data . ipynbs . id !== 'string' ||
258+ data . ipynbs . id . length > 20
259+ )
260+ return res . status ( 400 ) . send ( 'malformed request' )
261+
262+ const pendingupdates = [ ]
263+ const changes = {
264+ $currentDate : { lastaccess : true } ,
265+ $set : { }
266+ }
267+ let changed = false
268+ if ( typeof data . ipynbs . presentDownload !== 'undefined' ) {
269+ changes . $set [ 'ipynbs.$[notebook].presentDownload' ] =
270+ data . ipynbs . presentDownload
271+ changed = true
272+ }
273+ if ( typeof data . ipynbs . name !== 'undefined' ) {
274+ changes . $set [ 'ipynbs.$[notebook].name' ] = data . ipynbs . name
275+ changed = true
276+ }
277+ if ( 'note' in data . ipynbs ) {
278+ changes . $set [ 'ipynbs.$[notebook].note' ] = data . ipynbs . note
279+ changed = true
280+ }
281+ if ( changed ) {
282+ pendingupdates . push (
283+ lecturescol . updateOne ( { uuid : lectureuuid } , changes , {
284+ arrayFilters : [ { 'notebook.id' : data . ipynbs . id } ]
285+ } )
286+ )
287+ }
288+
289+ if ( data . ipynbs . applets ) {
290+ data . ipynbs . applets . forEach ( ( applet ) => {
291+ if (
292+ typeof applet . presentToStudents !== 'undefined' &&
293+ applet . id
294+ ) {
295+ pendingupdates . push (
296+ lecturescol . updateOne (
297+ { uuid : lectureuuid } ,
298+ {
299+ $set : {
300+ 'ipynbs.$[notebook].applets.$[applet].presentToStudents' :
301+ applet . presentToStudents
302+ } ,
303+ $currentDate : { lastaccess : true }
304+ } ,
305+ {
306+ arrayFilters : [
307+ { 'notebook.id' : data . ipynbs . id } ,
308+ { 'applet.appid' : applet . id }
309+ ]
310+ }
311+ )
312+ )
313+ }
314+ } )
315+ }
316+ await Promise . all ( pendingupdates )
317+ /* {
318+ name: el.name,
319+ id: el.id,
320+ mimetype: el.mimetype,
321+ filename: el.filename,
322+ date: el.data,
323+ presentDownload: el.presentDownload,
324+ applets: el.applets?.map?.((applet) => ({
325+ appid: applet.appid,
326+ appname: applet.appname,
327+ presentToStudents: applet.presentToStudents
328+ }))
329+ } */
330+ }
331+ if ( data . removeipynb ) {
332+ if ( data . ipynbs ) return res . status ( 400 ) . send ( 'malformed request' ) // please not simultaneously
333+ if (
334+ ! data . removeipynb . id ||
335+ typeof data . removeipynb . id !== 'string' ||
336+ data . removeipynb . id . length > 20
337+ )
338+ return res . status ( 400 ) . send ( 'malformed request' )
339+ await lecturescol . updateOne (
340+ { uuid : lectureuuid } ,
341+ {
342+ $currentDate : { lastaccess : true } ,
343+ $pull : { ipynbs : { id : data . removeipynb . id } }
344+ }
345+ )
346+ const removed = details ?. ipynbs ?. find (
347+ ( el ) => el . id === data . removeipynb . id
348+ )
349+ if ( removed ?. sha ) {
350+ const ipynb = [ removed . sha . toString ( 'hex' ) ]
351+ await this . redis . sAdd ( 'checkdel:ipynb' , ipynb )
352+ }
353+ }
254354 if ( data . polls ) {
255355 // console.log(data.polls)
256356 if (
257357 ! data . polls . id ||
258358 typeof data . polls . id !== 'string' ||
259- data . polls . id > 20 ||
260- ( ! data . polls . name && ! ( 'multi' in data . polls ) )
359+ data . polls . id . length > 20 ||
360+ ( ! data . polls . name &&
361+ ! ( 'multi' in data . polls ) &&
362+ ! ( 'note' in data . polls ) )
261363 )
262364 return res . status ( 400 ) . send ( 'malformed request' )
263365
@@ -297,6 +399,8 @@ export class AppHandler {
297399 const tochange = { $set : { } , $currentDate : { lastaccess : true } }
298400 if ( data . polls . name )
299401 tochange . $set [ 'polls.$[pollchange].name' ] = data . polls . name
402+ if ( 'note' in data . polls )
403+ tochange . $set [ 'polls.$[pollchange].note' ] = data . polls . note
300404 if ( 'multi' in data . polls )
301405 tochange . $set [ 'polls.$[pollchange].multi' ] = data . polls . multi
302406 await lecturescol . updateOne ( { uuid : lectureuuid } , tochange , {
@@ -309,7 +413,7 @@ export class AppHandler {
309413 if (
310414 ! data . removepolls . id ||
311415 typeof data . removepolls . id !== 'string' ||
312- data . removepolls . id > 20
416+ data . removepolls . id . length > 20
313417 )
314418 return res . status ( 400 ) . send ( 'malformed request' )
315419 const tochange = { $currentDate : { lastaccess : true } }
@@ -364,7 +468,40 @@ export class AppHandler {
364468 if ( details . date ) {
365469 toret . date = details . date
366470 }
367- // TODO add additional fields for instructor such as pools, pictures, pdfbackground etc.
471+ if ( details . ipynbs ) {
472+ // 'application/x-ipynb+json'
473+ const instructor = req . token . role . includes ( 'instructor' )
474+ const retipynbs = details . ipynbs
475+ . map ( ( el ) => {
476+ return {
477+ name : el . name ,
478+ id : el . id ,
479+ sha : el . sha ,
480+ mimetype : el . mimetype ,
481+ filename : el . filename ,
482+ date : el . data ,
483+ presentDownload : el . presentDownload ,
484+ note : el . note ,
485+ applets : el . applets
486+ ?. map ?. ( ( applet ) => ( {
487+ appid : applet . appid ,
488+ appname : applet . appname ,
489+ presentToStudents : applet . presentToStudents
490+ } ) )
491+ ?. filter ?. ( ( applet ) => applet . presentToStudents || instructor )
492+ }
493+ } )
494+ . filter (
495+ ( el ) => instructor || el . presentDownload || el . applets ?. length > 0
496+ )
497+ . map ( ( el ) => ( {
498+ sha : el . sha . buffer . toString ( 'hex' ) ,
499+ ...el ,
500+ url : el . sha && this . getFileURL ( el . sha . buffer , el . mimetype )
501+ } ) )
502+ toret . ipynbs = retipynbs
503+ }
504+ // add additional fields for instructor such as pools, pictures, pdfbackground etc.
368505 if ( req . token . role . includes ( 'instructor' ) ) {
369506 // fields only available for the instructors
370507 if ( details . pictures ) {
@@ -572,10 +709,14 @@ export class AppHandler {
572709 if ( req . body . what === 'polls' || req . body . what === 'all' ) {
573710 filter = { ...filter , polls : 1 }
574711 }
712+ if ( req . body . what === 'ipynbs' || req . body . what === 'all' ) {
713+ filter = { ...filter , ipynbs : 1 }
714+ }
575715 if ( req . body . what === 'lecture' || req . body . what === 'all' ) {
576716 filter = {
577717 ...filter ,
578718 usedpictures : 1 ,
719+ usedipynbs : 1 ,
579720 backgroundpdfuse : 1 ,
580721 backgroundpdf : 1 ,
581722 boards : 1 ,
@@ -632,6 +773,24 @@ export class AppHandler {
632773 )
633774 }
634775 }
776+ if ( req . body . what === 'ipynbs' || req . body . what === 'all' ) {
777+ if ( lecturedoc . ipynbs ) {
778+ const toremove = lecturedoc . ipynbs . map ( ( el ) => el . sha )
779+ promis . push (
780+ lecturescol . updateOne (
781+ { uuid : lectureuuid } ,
782+ { $pull : { ipynbs : { sha : { $in : toremove } } } }
783+ )
784+ )
785+ // now we removed dublettes, we can insert the current ones
786+ promis . push (
787+ lecturescol . updateOne (
788+ { uuid : lectureuuid } ,
789+ { $push : { ipynbs : { $each : lecturedoc . ipynbs } } }
790+ )
791+ )
792+ }
793+ }
635794 if ( req . body . what === 'lecture' || req . body . what === 'all' ) {
636795 const set = { }
637796 if ( lecturedoc . backgroundpdfuse ) set . backgroundpdfuse = 1
@@ -640,6 +799,7 @@ export class AppHandler {
640799 if ( lecturedoc . boardsavetime )
641800 set . boardsavetime = lecturedoc . boardsavetime
642801 if ( lecturedoc . usedpictures ) set . usedpictures = lecturedoc . usedpictures
802+ if ( lecturedoc . usedipynbs ) set . usedipynbs = lecturedoc . ipynbs
643803
644804 const boards = [ ]
645805 if ( lecturedoc . boards ) {
@@ -788,6 +948,89 @@ export class AppHandler {
788948 return res . status ( 500 ) . send ( 'error converting ' + error )
789949 }
790950 } )
951+
952+ app . post ( path + '/lecture/ipynb' , async ( req , res ) => {
953+ if ( ! req . token . role . includes ( 'instructor' ) )
954+ return res . status ( 401 ) . send ( 'unauthorized' )
955+ const lectureuuid = req . token . course . lectureuuid
956+ if ( ! isUUID ( lectureuuid ) ) return res . status ( 401 ) . send ( 'unauthorized uuid' ) // supply valid data
957+
958+ // first we have to check, whether some information about the applets is already set
959+ const details = await this . getLectureDetails ( lectureuuid )
960+ if ( ! details ) return res . status ( 404 ) . send ( 'not found' )
961+ try {
962+ const body = { }
963+ const [ { sha256, mimeType } = { } ] = await this . handleFileUpload (
964+ req ,
965+ body ,
966+ { filename : true , id : true , applets : true , name : true } ,
967+ { } ,
968+ [ 'file' ] ,
969+ this . maxFileSize ,
970+ [ 'application/x-ipynb+json' ]
971+ )
972+
973+ const applets = JSON . parse ( body . applets )
974+ let oldsha
975+ const oldNotebook = details ?. ipynbs ?. find ( ( el ) => el . id === body . id )
976+ if ( oldNotebook ?. sha ) {
977+ oldsha = oldNotebook ?. sha
978+ }
979+ applets ?. forEach ?. ( ( applet ) => {
980+ if ( typeof applet . presentToStudents !== 'undefined' ) return
981+ const oldApplet = oldNotebook ?. applets ?. find ?. (
982+ ( appl ) => appl . appid === applet . appid
983+ )
984+ if ( typeof oldApplet ?. presentToStudents !== 'undefined' )
985+ applet . presentToStudents = oldApplet . presentToStudents
986+ else applet . presentToStudents = false
987+ } )
988+ const pynb = {
989+ id : body . id ,
990+ name : body . name ,
991+ sha : sha256 ,
992+ mimetype : mimeType ,
993+ filename : body . filename ,
994+ presentDownload :
995+ oldNotebook ?. presentDownload || body . presentDownload || 'no' ,
996+ note : oldNotebook ?. note || '' ,
997+ applets
998+ }
999+ const lecturescol = this . mongo . collection ( 'lectures' )
1000+ // date
1001+ if ( ! details ?. ipynbs ) {
1002+ await lecturescol . updateOne (
1003+ { uuid : lectureuuid } ,
1004+ {
1005+ $addToSet : {
1006+ ipynbs : { id : pynb . id }
1007+ } ,
1008+ $currentDate : { lastaccess : true }
1009+ }
1010+ )
1011+ }
1012+ await lecturescol . updateOne (
1013+ { uuid : lectureuuid } ,
1014+ {
1015+ $set : {
1016+ 'ipynbs.$[elem]' : { id : pynb . id , ...pynb }
1017+ } ,
1018+ $currentDate : { lastaccess : true }
1019+ } ,
1020+ {
1021+ arrayFilters : [ { 'elem.id' : pynb . id } ]
1022+ }
1023+ )
1024+ if ( oldsha ) {
1025+ const ipynb = [ oldsha . toString ( 'hex' ) ]
1026+ await this . redis . sAdd ( 'checkdel:ipynb' , ipynb )
1027+ }
1028+ return res . status ( 200 ) . json ( { } ) // no return just success
1029+ } catch ( error ) {
1030+ console . log ( 'upload ipynb error' , error )
1031+ return res . status ( 500 ) . send ( 'error uploading ipynb ' + error )
1032+ }
1033+ } )
7911034 }
7921035
7931036 async getLectureDetails ( uuid ) {
0 commit comments