Dans ce cas, @Ben Spead propose d'installer Python sur le serveur  avec la bibliothèque que l'on souhaitez utiliser et d'appeler le script Python via $zf(-100). Plus tard, lorsque d'une mise à niveau de l'environnement, il sera possible de passer directement à l’utilisation de Embedded Python.

@Luis Angel Pérez Ramos propose une autre solution, hors Embedded Python : 

Eh bien, si votre instance IRIS ne prend pas en charge Embedded Python, vous pouvez procéder comme suit (si vous utilisez un serveur Linux) :

  1. Installez une application comme ImageMagick sur votre serveur
  2. À partir de votre opération commerciale, utilisez la fonction $ZF(-100) pour exécuter sur le serveur la commande magique permettant de convertir le PDG en JPG, quelque chose comme :
    $ZF(-100,"","magick","\usr\image.pdf","\usr\image.jpg")
  3. Peut-être que cela prend un certain temps, vous pouvez attendre la fin de l'exécution ou simplement créer un service métier pour obtenir tous les nouveaux fichiers PDF.

Réponse de @Rob Tweed 

Nous avons deux produits Open Source qui prendront en charge les JWT pour vous de la manière correspondant à ce que vous demandez (c'est-à-dire les services REST avec IRIS) :

- QEWD, si vous souhaitez tout implémenter en back-end dans Node.js / JavaScript

- mgweb-server si vous souhaitez utiliser la logique ObjectScript pour votre logique back-end

Pour QEWD et IRIS, voir :

https://github.com/robtweed/qewd-starter-kit-iris-networked

En particulier pour les services REST, voir :

https://github.com/robtweed/qewd-starter-kit-iris-networked/blob/master/...

et plus précisément cette section:

https://github.com/robtweed/qewd-starter-kit-iris-networked/blob/master/...

Pour le serveur mgweb, voir :

https://github.com/robtweed/mgweb-server

spécifiquement utilisé avec IRIS :

https://github.com/robtweed/mgweb-server/blob/master/IRIS.md

et dans ce document, cette section sur les JWT :

https://github.com/robtweed/mgweb-server/blob/master/IRIS.md#using-json-...

Rob

Bonjour @Julia Pertin,

si j'ai bien compris ton besoin, pour régler ton problème qui consiste à éviter de récupérer plusieurs fois le même flux json, ton Business Service doit servir uniquement à appeler un Business Process, qui lui va se charger de récupérer le flux json pour le passer à une Business Operation.

Dans le cas où ton Business Service appelle le Business Process de manière synchrone, il attendra bien la réponse avant de s'exécuter à nouveau.
Dans le cas contraire, c'est à dire si le Business Process est appelé de manière asynchrone, le Business Service fera un nouvel appel à chaque atteinte du temps prévu par le paramètre "
INTERVALLE ENTRE APPELS".

  

Le code du Business Service :
 

Class EP.service.replication Extends Ens.BusinessService
{

Property Adapter As Ens.InboundAdapter;Parameter ADAPTER = "Ens.InboundAdapter";Property TargetConfigNames As%String(MAXLEN = 1000);Property sync As%Boolean [ InitialExpression = 1 ];Parameter SETTINGS = "sync:Advanced,TargetConfigNames:Basic:selector?multiSelect=1&context={Ens.ContextSearch/ProductionItems?targets=1&productionName=@productionId}";

Method OnProcessInput(pInput As%RegisteredObject, Output pResponse As Ens.StringResponse) As%Status
{
  set tSC = $$$OKfor iTarget=1:1:$L(..TargetConfigNames, ",") {
        set tOneTarget=$ZStrip($P(..TargetConfigNames,",",iTarget),"<>W")  Continue:""=tOneTarget
        $$$TRACE("The target '"_tOneTarget_"' will be called "_$SELECT(..sync=1:"synchronously",..sync=0:"synchronously"))
        if..sync {
          set tSC1=..SendRequestSync(tOneTarget,pInput,.pResponse)  Set:$$$ISERR(tSC1) tSC=$$$ADDSC(tSC,tSC1)
        } else {
          set tSC1=..SendRequestAsync(tOneTarget,pInput)  Set:$$$ISERR(tSC1) tSC=$$$ADDSC(tSC,tSC1)
        }
  }
  return tSC
}

/// Return an array of connections for drawing lines on the config diagramClassMethod OnGetConnections(Output pArray As%String, pItem As Ens.Config.Item)
{
	Do##super(.pArray,pItem)
	If pItem.GetModifiedSetting("TargetConfigNames",.tValue) {
		For i=1:1:$L(tValue,",") { Set tOne=$ZStrip($P(tValue,",",i),"<>W")  Continue:""=tOne  Set pArray(tOne)="" }
	}
}

}

Le code du Business Process

Class EP.process.replication Extends Ens.BusinessProcess [ ClassType = persistent, ProcedureBlock ]
{

Property Server As%String [ InitialExpression = "host.docker.internal" ];Property Port As%Integer [ InitialExpression = 12773 ];Property classname As%String [ InitialExpression = "data.client" ];Parameter SETTINGS = "Server:target,Port:target,classname:target";

Method OnRequest(pRequest As Ens.Request, Output pResponse As Ens.StringContainer) As%Status
{
    set sc = $$$OKset tHttpRequest             = ##class(%Net.HttpRequest).%New()
    set pContainer               = ##class(Ens.StreamContainer).%New()
    set stream                   = ##class(%Stream.GlobalCharacter).%New()
    set tHttpRequest.Server      = ..Serverset tHttpRequest.Port        = ..Portset location                 = "/common/list/" _ ..classname _ "/json"set tHttpRequest.ContentType = "application/json"set tHttpRequest.Timeout     = 900set tSC = tHttpRequest.Get(location)
  
   if$ISOBJECT(tHttpRequest.HttpResponse) {
    Set jsonObject               = [].%FromJSON(tHttpRequest.HttpResponse.Data)
    Do jsonObject.%ToJSON(.stream)
    set pContainer.Stream = stream
    $$$TRACE("stream size:"_stream.Size)
    $$$TRACE("stream data:"_stream.Read(1000))

    set sc = ..SendRequestSync("EP.operation.replication",pContainer,.pResponse)
    
   }
   return sc
}

Storage Default
{
<Data name="replicationDefaultData">
<Subscript>"replication"</Subscript>
<Value name="1">
<Value>Server</Value>
</Value>
<Value name="2">
<Value>Port</Value>
</Value>
<Value name="3">
<Value>classname</Value>
</Value>
</Data>
<DefaultData>replicationDefaultData</DefaultData>
<Type>%Storage.Persistent</Type>
}

}

Le code de la Business Opération

Class EP.operation.replication Extends Ens.BusinessOperation
{

Property Adapter As Ens.OutboundAdapter;Parameter ADAPTER = "Ens.OutboundAdapter";Parameter INVOCATION = "Queue";

Method sync(pRequest As Ens.StreamContainer, Output pResponse As Ens.StringContainer) As%Status
{
    set pResponse = ##class(Ens.StringContainer).%New()
    set json = [].%FromJSON(pRequest.Stream)
    set array = json.%GetIterator()
    while array.%GetNext(.key,.value) {
        $$$TRACE("Data:"_value.%ToJSON())
        set a = ##class(data.client2).%New()
        set sc = a.%JSONImport(value)
        if 'sc {
            $$$LOGERROR("ERROR while importing:"_$system.Status.GetErrorText(sc))
        } else {
            set sc = a.%Save()
        }
    }
    set pResponse.StringValue = key _" records inserted"return sc
}

XData MessageMap
{
<MapItems>
    <MapItem MessageType="Ens.StreamContainer">
        <Method>sync</Method>
    </MapItem>
</MapItems>
}

}

Bonjour @Julia Pertin 
peux-tu me donner plus de détails sur l'erreur rencontrée ?

Vérifies-bien si tu recrées une production from scratch, en ajoutant service-process-operation, de conserver les noms les BS-BP-BO identiques aux noms des 3 classes EP.service.replication, EP.process.replication et EP.operation.replication, et qu'ils sont bien reliés entre eux.
 

Réponse de @Ashok Kumar T 

Bonjour @Muhammad Waseem 

Vous pouvez peut-être essayer d'extraire la ressource patient du bundle ou l'utiliser si c'est déjà une ressource. Chargez la classe du modèle patient FHIR HS.FHIR.DTL.vR4.Model.Resource.Patient avec la ressource extraite. Ensuite, vous pouvez convertir la date de naissance et valider l'âge. Vous pouvez utiliser la même logique dans votre code DTL dans un processus FHIR entre le service FHIR (HS.FHIRServer.Interop.Service) et l'opération FHIR (HS.FHIRServer.Interop.Operation).

ClassMethod VaidatePatientResource(patientResourceStreram As %Stream.Object)
{
	#dim patient As HS.FHIR.DTL.vR4.Model.Resource.Patient
	try {
		set patient = ##class(HS.FHIR.DTL.vR4.Model.Resource.Patient).FromJSON(patientResourceStreram)
		Set age = $horolog - $ZdateH(patient.birthDate,3)\360
		if age<18 $$$ThrowStatus($$$ERROR($$$GeneralError, "Age is less than 18"))
	}
	catch ex {
		w ex.DisplayString()
	}
}

Bonjour @Jean-Charles.Cano,

Stream n'est pas une méthode mais une propriété ; aussi, tout devrait aller mieux après avoir remplacé cette ligne : 

set esc = ..Adapter.PutStream(fileName, pRequest.Stream())

par celle-ci :

set esc = ..Adapter.PutStream(fileName, pRequest.Stream)

NB: si tu rencontres d'autres erreurs, pense à les indiquer clairement dans les échanges afin qu'elles soient référencées ; cela permet de les retrouver plus facilement lors des recherches de pb similaires déjà rencontrés.

 

Exemple de classe utilisant un index IDKEY

 

Test

IRISAPP>w##class(data.centre).test()
^data.centreD("departement",83)=10^data.centreD("departement",83,1)=$lb("","The industry leader in just-in-time seven-sigma Internet content for social networks.")
^data.centreD("departement",83,2)=$lb("","On-line distributors of open distributed Internet connectivity for capital markets.")
^data.centreD("departement",83,3)=$lb("","Developers of cutting-edge HTML5 XML instruments for our long-term clients.")
^data.centreD("departement",83,4)=$lb("","Building shareholder value by delivering advanced financial application development media for industry and academia.")
^data.centreD("departement",83,5)=$lb("","Providers of breakthrough satellite-based advanced connectivity for industry and academia.")
^data.centreD("departement",83,6)=$lb("","Resellers of cloud-based seven-sigma database connectivity for the enterprise.")
^data.centreD("departement",83,7)=$lb("","Developers of dynamic object-oriented graphical services for social networks.")
^data.centreD("departement",83,8)=$lb("","Developers of high-performance nano-advanced productivity tools for the Fortune 50.")
^data.centreD("departement",83,9)=$lb("","Specializing in the development and manufacturing of breakthrough wireless forecasting marketing services for the enterprise.")
^data.centreD("departement",83,10)=$lb("","Enabling individuals and businesses to manage just-in-time big data Internet technologies for discriminating investors.")
1
IRISAPP>w##class(data.centre).test(3)
^data.centreD("departement",83)=13^data.centreD("departement",83,1)=$lb("","The industry leader in just-in-time seven-sigma Internet content for social networks.")
^data.centreD("departement",83,2)=$lb("","On-line distributors of open distributed Internet connectivity for capital markets.")
^data.centreD("departement",83,3)=$lb("","Developers of cutting-edge HTML5 XML instruments for our long-term clients.")
^data.centreD("departement",83,4)=$lb("","Building shareholder value by delivering advanced financial application development media for industry and academia.")
^data.centreD("departement",83,5)=$lb("","Providers of breakthrough satellite-based advanced connectivity for industry and academia.")
^data.centreD("departement",83,6)=$lb("","Resellers of cloud-based seven-sigma database connectivity for the enterprise.")
^data.centreD("departement",83,7)=$lb("","Developers of dynamic object-oriented graphical services for social networks.")
^data.centreD("departement",83,8)=$lb("","Developers of high-performance nano-advanced productivity tools for the Fortune 50.")
^data.centreD("departement",83,9)=$lb("","Specializing in the development and manufacturing of breakthrough wireless forecasting marketing services for the enterprise.")
^data.centreD("departement",83,10)=$lb("","Enabling individuals and businesses to manage just-in-time big data Internet technologies for discriminating investors.")
^data.centreD("departement",83,11)=$lb("","On-line distributors of scalable predictive analytic XML gaming for our long-term clients.")
^data.centreD("departement",83,12)=$lb("","On-line distributors of interactive quantum advanced media for the pharmaceutical industry.")
^data.centreD("departement",83,13)=$lb("","Building shareholder value by delivering innovative distributed cold-fusion powered gaming for the Financial community.")
1
Class data.centre Extends%Persistent
{

/// Description
Index IdKeyIndex On (APP, DD, NNN) [ IdKey ];/// Nom de l'application utilisée par le centreProperty APP As%String;/// n° du département du centreProperty DD As%Integer;/// n° séquentiel dans le départementProperty NNN As%Integer;Property description As%String(MAXLEN = "");ClassMethod create(APP As%String, DD As%Integer, description As%String) As%Status
{
    set a = ..%New()
    set a.APP = APP
    set a.DD = DD
    set a.NNN = $INCREMENT(^data.centreD(APP,DD))
    set a.description = description
    set sc = a.%Save()
    return sc
}

ClassMethod test(nb As%Integer = 10) As%Status
{
    for i=1:1:nb {
        set sc = ..create("departement",83,##class(%PopulateUtils).Mission())
    }
    zw^data.centreD
    return sc
}

Storage Default
{
<Data name="centreDefaultData">
<Value name="1">
<Value>%%CLASSNAME</Value>
</Value>
<Value name="2">
<Value>description</Value>
</Value>
</Data>
<DataLocation>^data.centreD</DataLocation>
<DefaultData>centreDefaultData</DefaultData>
<IdLocation>^data.centreD</IdLocation>
<IndexLocation>^data.centreI</IndexLocation>
<StreamLocation>^data.centreS</StreamLocation>
<Type>%Storage.Persistent</Type>
}

}

2023 a été une très belle année pour Developer Community, qui a vu le nombre de ses membres s'accroître fortement, dans toutes ses pays, avec une augmentation de la qualité des articles, des commentaires et des applications sur Open Exchange.
2024 sera encore plus riche en contenus et en solutions 

Bonjour @Pierre LaFay 
en m'inspirant des liens proposés par @Guillaume Rongier tu trouveras ci-dessous et en ligne un exemple de génération de feuille Excel à partir de openpyxl 

 

Code

/// d ##class(apptools.python.xlsx).test()Class apptools.python.xlsx
{

Parameter DATADIRECTORY = "/home/irisowner/dev/data/";/// d ##class(apptools.python.xlsx).test()ClassMethod test(name As%String = "titanicTest") As%Status
{
 set name=name_$INCREMENT(^RunAppTest(name))
 set sql="select * FROM dc_demo.titanic"
#;  set sql="select name,sex,age FROM dc_demo.titanic"set gn="^||tmp",format=",n,n,n,n,s5,s5,s40,s5,s50"set format("freeze")="B3";freezeset format("title")="Title "_sql
 set format("sheetname")="test"
#;  do ##class(apptools.core.sys).SaveSQL(sql,gn)set exec="##class(apptools.python.xlsx).MarkRed(.%AppLogInfoVal, .%AppLogInfoCol, .%AppLogInfoHead, .%AppLogInfoTemp)"
#;  do ##class(apptools.python.xlsx).gn2xlsx(gn,.format,..#DATADIRECTORY_"___gn2xlsx*.xlsx","Title "_sql,,,,exec)set format("freeze")="B4";freezedo##class(apptools.python.xlsx).sql2xlsx(sql,.format,..#DATADIRECTORY_name_".xlsx")
 do..toPDF(name)
 return$$$OK
}

ClassMethod sql2xlsx(sql, format = "", file As%String = "/home/irisowner/dev/data/sample-py-*.xlsx", title = "")
{
	set st=$$$OKset statement = ##CLASS(%SQL.Statement).%New()
	set status=statement.%Prepare(sql)
	if$$$ISERR(status) {write"%Prepare failed:"do$SYSTEM.Status.DisplayError(status) quit}

	set rset=statement.%Execute()
	if (rset.%SQLCODE '= 0) {write"%Execute failed:", !, "SQLCODE ", rset.%SQLCODE, ": ", rset.%Messagequit}

	;prepare XLSXtry {
        set openpyxl = ##class(%SYS.Python).Import("openpyxl") 
    } catch err {
        do..InstallPy("openpyxl")
        set openpyxl = ##class(%SYS.Python).Import("openpyxl") 
    }
	set wb = openpyxl.Workbook()
	#; grab the active worksheetset ws = wb.active
	set ws.title = $select($D(format("sheetname"),ds):ds,1:"sheetname")
	set pybuiltins = $system.Python.Import("builtins")
	set pycols = pybuiltins.list()
	do pycols.append(" ")
	do ws.append(pycols)

	set pycols = pybuiltins.list()
	set (count)=0set ColCount=statement.%Metadata.columns.Count()
	for i=1:1:ColCount {	
		do pycols.append(statement.%Metadata.columns.GetAt(i).colName)
		set col=openpyxl.utils."get_column_letter"(i)
	}
	do ws.append(pycols)

	while (rset.%Next())	{
		set count=count+1set pyrows = pybuiltins.list()
		for ii=1:1:ColCount {
			set val=..CleanCtrl(rset.%GetData(ii))
			if$e($P(format,",",ii),1)="d" { //dateset val=..ToPyDate(val)
			}
			elseif$e($P(format,",",ii),1,2)="dt" { //datetimeset val=..ToPyDateTime(val)
			}
			elseif$e($P(format,",",ii),1)="n" { //numberset val=+val
			}
			do pyrows.append(val)
		}
		do ws.append(pyrows)
	}
   ;}if$g(format("freeze"))'="" {
		; set builtins = $system.Python.Import("builtins") zwrite builtins.type(ws)  zwrite builtins.dir(wb)set ws."freeze_panes" = pybuiltins.str(format("freeze")) ;format("freeze");set ws."freeze_panes" = "B2" ;format("freeze")
	}
	else {
		set ws."freeze_panes" = pybuiltins.str("B2") ;default
	}
	if file["*"set file=$replace(file,"*","-"_$zd($h,3)_"_"_$tr($zt($p($h,",",2),1),":"))
	write !,"Save into "_file_" rows: "_count
	do wb.save(file)
	
	return$GET(count)
}

ClassMethod toPDF(file As%String) As%Status [ Language = python ]
{
  import pandas as pd
  from csv2pdf import convert
  PATH = '/home/irisowner/dev/data/'
  PATH_TO_XLSX = PATH+file+'.xlsx'
  PATH_TO_CSV = PATH+file+'.csv'
  PATH_TO_PDF = PATH+file+'.pdf'
  
  read_file = pd.read_excel (PATH_TO_XLSX)
  read_file.to_csv (PATH_TO_CSV, index = None, header=True)
  convert(PATH_TO_CSV , PATH_TO_PDF)
}

/// set gn="^||tmp",format="n,s150,,,,,,,,d," /// set format("freeze")="B5" ;freeze/// d ##class(apptools.core.sys).SaveSQL("select * from apptools_core.Log order by id desc",gn)/// d ##class(apptools.python.xlsx).gn2xlsx(gn,.format,"/iris-backup/temp/test*.xlsx","Test")/// Example coloring a column values if is not null///  set exec="##class(apptools.python.xlsx).MarkRed(.%AppLogInfoVal, %AppLogInfoCol, %AppLogInfoHead, .%AppLogInfoTemp)"ClassMethod MarkRed(Val, Col, Head, openpyxl)
{
	s res=Val
	if$g(Head) {
		if res.value="DispatchClass"set Col("DispatchClass",Col)=""
	}
	else {
		if$Data(Col("DispatchClass",Col)) {
			if res.value'="" {
				;set argsfill = {"start_color":"D3D3D3", "end_color":"D3D3D3", "fill_type":"solid"};set fill = openpyxl.styles.PatternFill(argsfill...)	set argsfont = {"color":"DC143C", "bold":true, "italic":false, "size":11} ;color Crimsonset font = openpyxl.styles.Font(argsfont...)
				;set res.value=$FN(res.value,"",2);set res.fill=fillset res.font=font
			}
		}
	}
	q res
}

/// do ##class(apptools.python.xlsx).InstallPy("openpyxl")ClassMethod InstallPy(lib)
{
    //  depricated  ;set sc = ##class(%SYS.Python).Install("openpyxl")set cmd="pip3 install --target /usr/irissys/mgr/python/ "_lib
    if$zversion(1)'=3 {
        set tMgrDir = $System.Util.ManagerDirectory()
		set tBinDir = $System.Util.BinaryDirectory()_"irispip.exe"set cmd=tBinDir_" install --target "_tMgrDir_"python "_lib
    }
    set st=##class(%ZPM.PackageManager.Developer.Utils).RunCommandViaZF(cmd,.tLog,.tErr)
    quit$$$OK
}

/// Date YYYY-MM-DD HH:MM:SS to Python dt.datetimeClassMethod ToPyDateTime(val, ByRef dt)
{
	quit:'val val
	if val["."set val=$p(val,".",3)_"-"_$p(val,".",2)_"-"_$p(val,".",1) // format YYYY-MM-DDif val'["-"set val=$zd(+val,3) // $h formatset dt = ##class(%SYS.Python).Import("datetime")
	set val=dt.datetime(+$p(val,"-",1), +$p(val,"-",2), +$p($p(val,"-",3)," "),+$p($p(val,":",1)," ",2),+$p($p(val,":",2)," ",2),+$p($p(val,":",3)," ",2))
	quit val
}

/// Date YYYY-MM-DD to Python dt.dateClassMethod ToPyDate(val, ByRef dt)
{
	quit:'val val
	if val["."set val=$p(val,".",3)_"-"_$p(val,".",2)_"-"_$p(val,".",1) // format YYYY-MM-DDif val'["-"w111_val_111 set val=$zd(+val,3) // $h formatset dt = ##class(%SYS.Python).Import("datetime")
	set val=dt.date(+$p(val,"-",1), +$p(val,"-",2), +$p($p(val,"-",3)," "))
	quit val
}

ClassMethod CleanCtrl(val)
{
	quit$zstrip(val,"*C")
}

}

Une fois obtenu le fichier Excel, il est possible de le convertir en CSV puis en PDF en utilisant les librairies pandas et csv2pdf

ClassMethod toPDF(file As%String) As%Status [ Language = python ]
{
  import pandas as pd
  from csv2pdf import convert
  PATH = '/home/irisowner/dev/data/'
  PATH_TO_XLSX = PATH+file+'.xlsx'
  PATH_TO_CSV = PATH+file+'.csv'
  PATH_TO_PDF = PATH+file+'.pdf'
  
  read_file = pd.read_excel (PATH_TO_XLSX)
  read_file.to_csv (PATH_TO_CSV, index = None, header=True)
  convert(PATH_TO_CSV , PATH_TO_PDF)
}

@Guillaume Rongier ajoute :

Bonjour Rubén,

Une autre proposition sur IRIS 2021.1+ peut être celle-ci avec l'utilisation de la fonction nouvelle fenêtre (OVER) :

ClassMethod getPersonsPagWindow(iAge As%Integer, sortField As%String = 1, sortOrder As%String = 2, pageSize As%String = 20, pageIndex As%String = 1) As%DynamicObject
{
    set out = []
    set vFrom = ((pageIndex -1 ) * pageSize)+1set vTo = vFrom + (pageSize-1)

    set sql = "SELECT * "_
                "FROM ( SELECT persons.* "_
                "        , ROW_NUMBER() OVER (ORDER By "_sortField_" "_ $CASE(sortOrder,1:"ASC",2:"DESC",:"ASC")_
                "    ) rn "_
                "        FROM Sample.Person persons where Age > ? "_
                "    ) tmp "_
                "WHERE rn between "_vFrom_" and "_vTo_" "_
                "ORDER By "_sortField_" "_ $CASE(sortOrder,1:"ASC",2:"DESC",:"ASC")

    Set rs=##class(%ResultSet).%New("%DynamicQuery:SQL")
    set sc = rs.Prepare(sql)
    set sc = rs.Execute(iAge) If$$$ISERR(sc) Do DisplayError^%apiOBJ(sc) Quitwhile rs.%Next() {
        Do out.%Push({
                "pid": (rs.%Get("ID")),
                "ssn" : (rs.%Get("SSN")),
                "lastname" : (rs.%Get("LastName")) ,
                "givenname":    (rs.%Get("GivenName")),
                "secondaryname":       (rs.%Get("SecondaryName")) ,
                "gender": (rs.%Get("Gender")),
                "age": (rs.%Get("Age") )
                })
    }

    set outJson = []
    Do outJson.%Push({
                "pageSize":(pageSize),
                "pageIndex":(pageIndex),
                "fromIndex":(vFrom),
                "toIndex":(vTo),
                "resultSet":(out)
                })
    return outJson
}

Je compare les deux solutions sur un dataset de 100 000 lignes sans index avec un résultat de 20 éléments en page 1 et voici les résultats :

"getPersonsPag timed : 1,647 secondes""getPersonsPagWindow timed : 0,247 secondes"

Je suppose que la fonction window est plus rapide car vous n'avez pas besoin de récupérer toutes les données de manière globale avant la pagination.

@Eduard Lebedyuk précise :

L'idée d'un ensemble de résultats défilants est d'appeler Save/OpenId - et l'ensemble de résultats continuera automatiquement sur une ligne suivante. Vous n'avez donc pas besoin de gérer les indices avant/arrière :

 

Un exemple ici

C'est également environ 3 fois plus rapide puisque la requête n'est exécutée qu'une seule fois : 

do##class(User.Pagination).Time("Save")
Save took 0,0048 sec
do##class(User.Pagination).Time("NoSave")
NoSave took 0,0143 sec

Bonjour @Jean-Charles.Cano,

les messages commençant par  "ConfigItem" font simplement partie des messages d'information dans le journal des événements indiquant le démarrage d'un service/process/operation dans la production (en précisant l'ID du processus de la tâche associée).
D'après tes captures d'écran, cela signifie que la connexion a échoué le 27/12 à 04:25 et que le service a correctement redémarré (message ConfigItem), le 28/12 à 14:45, ainsi que le même jour à 14:50, 16:27, etc.
Peut-être que le pb vient du paramètre  Rester connecté (StayConnected) = -1 

Le passer à une valeur positive ou à 0 règlera peut-être le pb.