accéder à la publication Guillaume Rongier · Oct 24, 2023 Bonjour, Je pense que c'est possible, cependant je n'ai pas encore eu l'occasion de le faire. L'idée est la suivante : Creer votre modèle Django en utilisant les classes Python qui vont bien. example : class MyModel(models.Model): # … fields … class Meta: managed = False # No database creation or deletion operations db_table = 'MyTable' Note : il faut bien mettre managed = False pour éviter que Django ne crée la table. Ou alors vous pouvez utiliser la commande django inspectdb pour générer le modèle Django à partir de votre base de données. python3 manage.py inspectdb ou encore python3 manage.py inspectdb > app_name/models.py
accéder à la publication Guillaume Rongier · Oct 24, 2023 Qu'entendez-vous par "éviter toute violation de sécurité"? Quelle violation de sécurité essayez-vous d'éviter? Créer une connexion à IRIS n'est pas une violation de sécurité. Vous pouvez spécifier l'utilisateur et les rôles dans la chaîne de connexion, vous pouvez donc limiter l'accès à la base de données à ce qui est nécessaire pour la tâche en cours.
accéder à la publication Guillaume Rongier · Oct 18, 2023 Je viens de faire la demande : https://github.com/intersystems-community/vscode-objectscript/issues/1254
accéder à la publication Guillaume Rongier · Oct 18, 2023 Salut, Pour le moment le langage parser objectscript permet de formater les fichiers avec la convention de nommage PascaleCase pour les commandes de ligne. Example : d ..SomeThing() set toto="titi" for value = "Red","Green","Blue" { Write value, ! } Apres format : Do ..SomeThing() Set toto="titi" For value = "Red","Green","Blue" { Write value, ! } L'indentation n'est pas encore pris en compte. Tu peux faire une demande d'enhancement ici : https://github.com/intersystems-community/vscode-objectscript/issues
accéder à la publication Guillaume Rongier · Oct 17, 2023 Bonjour, Pour compléter la réponse de Lorenzo, il y aussi les librairies suivantes : iris_tar iris_zip Ou encore utiliser du code python comme car vous utilisez iris 2021.2+: Class ZIP.demo Extends %RegisteredObject { ClassMethod Demo(pFileName As %String) As %Status { // Read a zip file into a stream set zipStream = ##class(%Stream.FileBinary).%New() // Link the stream to the file do zipStream.LinkToFile(pFileName) // Call the unzip method set sc = ##class(ZIP.demo).UnzipStream(zipStream, "/tmp/unzip") Quit sc } ClassMethod UnzipStream( pZipStream As %Stream.GlobalBinary, pDestDir As %String) As %Status { // Import python lib unzip set unzip = ##class(%SYS.Python).Import("zipfile") // Write the stream to a temporary file set tZipStream = ##class(%Stream.FileBinary).%New() do tZipStream.LinkToFile("/tmp/zipfile.zip") do tZipStream.CopyFromAndSave(pZipStream) // Create a new zip object set zip = unzip.ZipFile("/tmp/zipfile.zip") // Extract the zip file do zip.extractall(pDestDir) // Close the zip file do zip.close() Quit $$$OK } }
accéder à la publication Guillaume Rongier · Oct 16, 2023 Bonjour @Jean-Charles Cano , Pour le passage d'un stream à un autre, j'éviterai d'utiliser la fonction Write si vous ne bouclez pas sur le stream d'entrée. En effet, la fonction Write fonctionne par chunks, donc si vous n'avez pas lu tout le stream d'entrée, vous risquez de ne pas avoir tout écrit dans le stream de sortie. Comme vous utilisez la classe Ens.StreamContainer, vous pouvez utiliser le constructeur qui prend en paramètre un stream d'entrée et qui copie le contenu complet du stream d'entrée dans la variable de Stream du StreamContainer. Method OnProcessInput(pInput As %RegisteredObject, Output pOutput As %RegisteredObject) As %Status { Set sc = $$$OK set res = ##class(Ens.StringResponse).%New() set request = ##class(%Net.HttpRequest).%New() set response = ##class(%Net.HttpResponse).%New() set request.Https = 1 set request.Server = ..ServerAddress set request.Port = ..Port set request.SSLConfiguration = ..SSLConfiguration Try { set url = ..Path _ "/exportArticles_"_$ZDATE($HOROLOG,8)_".csv" set status = request.Get(url) $$$TRACE("request.HttpResponse.Data :"_request.HttpResponse.Data) #; Commented code to show the difference between the two methods #; set content = ##class(Ens.StreamContainer).%New() #; set stream = ##class(%Stream.GlobalCharacter).%New() #; do stream.Write(request.HttpResponse.Data) #; Recommended method set content = ##class(Ens.StreamContainer).%New(request.HttpResponse.Data) #; Other methods by looping on the stream #; set content = ##class(Ens.StreamContainer).%New() #; set stream = ##class(%Stream.GlobalCharacter).%New() #; while 'request.HttpResponse.Data.AtEnd { #; set stream.Write(request.HttpResponse.Data.Read()) #; } #; set content.Stream = stream $$$TRACE("sc : " _ request.HttpResponse.StatusCode) #; Old code #; $$$TRACE("stream size:"_stream.Size) #; set content.Stream = stream #; $$$TRACE("stream :"_stream.Read(1000)) #; New code $$$TRACE("content.Stream.Size :"_content.Stream.Size) $$$TRACE("content.Stream :"_content.Stream.Read(1000)) set sc = ..SendRequestSync(..TargetConfigNames, content) } Catch ex { set status= ex.AsStatus() $$$TRACE("erreur : "_status) } set result = request.HttpResponse.StatusCode if (result '= 200) { $$$TRACE("status: "_ result) } return $$$OK } Ensuite pour la partie écriture avec l'adapter FTP,vous pouvez utiliser la methode PutStream de l'adapter FTP. Class SupplyChain.exportArticleHaby Extends Ens.BusinessOperation { Property Adapter As EnsLib.FTP.OutboundAdapter; Parameter ADAPTER = "EnsLib.FTP.OutboundAdapter"; Parameter INVOCATION = "Queue"; Method putFileStream(pRequest As Ens.StreamContainer, Output pResponse As Ens.Response) As %Status { $$$TRACE("pRequest : "_ pRequest) Try { set fileName = "exportArticles_"_$ZDATE($HOROLOG,8)_".csv" set esc = ..Adapter.PutStream(fileName, pRequest.Stream) } Catch ex { $$$TRACE("statusCode: " _ esc) Set tSC=ex.AsStatus() } return $$$OK } XData MessageMap { <MapItems> <MapItem MessageType="Ens.StreamContainer"> <Method>putFileStream</Method> </MapItem> </MapItems> } }
accéder à la publication Guillaume Rongier · Oct 16, 2023 Bonjour Jules, Question très intéressante. as tu essayé d'inverser l'ordre d'héritage ? si non tu peux aussi utiliser le mot clé inheritance : https://docs.intersystems.com/iris20232/csp/docbook/Doc.View.cls?KEY=ROB... pour ordonner l'héritage de tes classes et peux etre résoudre ton problème. Sinon, une autre idée peut etre que plutot utiliser la surcharge de méthode pour l'audit, tu pourrais utiliser une macro comme ceci (l'idée me vient d'HealthShare): ROUTINE XXXX.TraceHelper [Type=INC] #def1Arg XXXXTRACE(%args) DO:..SendRequestAsync(%args) Apres tu peux utiliser la macro XXXXTRACE dans tes méthodes pour faire l'audit. exemple : Include XXXX.TraceHelper Class XXXX.OUTILS.BS.ComplexMap.FTP Extends (EnsLib.RecordMap.Service.ComplexBatchFTPService) { Parameter ADAPTER = "EnsLib.RecordMap.Service.FileServiceAdapter"; Method OnProcessInput(pInput As %RegisteredObject, Output pOutput As %RegisteredObject) As %Status { $$$XXXXTRACE("MyAudditTarget","MyauditMessage",0) Quit $$$OK } } Cordialement,
accéder à la publication Guillaume Rongier · Oct 12, 2023 est-il possible d'avoir un extrait de votre dockefile + des scripts de compilation ? une possible solution peut etre de s'assurer que la compliation est lieu dans le même layer docker : ARG IMAGE=intersystemsdc/irishealth-community:latest FROM $IMAGE USER root # Update package and install sudo RUN apt-get update && apt-get install -y \ nano \ python3-pip \ python3-venv \ sudo && \ /bin/echo -e ${ISC_PACKAGE_MGRUSER}\\tALL=\(ALL\)\\tNOPASSWD: ALL >> /etc/sudoers && \ sudo -u ${ISC_PACKAGE_MGRUSER} sudo echo enabled passwordless sudo-ing for ${ISC_PACKAGE_MGRUSER} # create dev directory WORKDIR /opt/irisapp RUN chown ${ISC_PACKAGE_MGRUSER}:${ISC_PACKAGE_IRISGROUP} /opt/irisapp USER ${ISC_PACKAGE_MGRUSER} # Copy source files to image COPY . /opt/irisapp # load demo stuff RUN iris start IRIS \ && iris session IRIS < /opt/irisapp/iris.script.test \ && iris session IRIS < /opt/irisapp/iris.script.supply \ && iris stop IRIS quietly iris.script.test : zn "TEST" // Load ObjectScript source files zw $SYSTEM.OBJ.ImportDir("/opt/irisapp/src/test", "*.cls", "cubk", .tErrors, 1) // exit script h iris.script.supply : zn "SUPPLY_CHAIN" // Load ObjectScript source files zw $SYSTEM.OBJ.ImportDir("/opt/irisapp/src/supply", "*.cls", "cubk", .tErrors, 1) dans l'example ci-dessus dans le dockerfile, les derniers lignes sont exécutées sur le meme layer docker avec deux scripts differents pour des questions de maintenabilité. Il est déconseillé de faire comme ci-dessous : ARG IMAGE=intersystemsdc/irishealth-community:latest USER root # Update package and install sudo RUN apt-get update && apt-get install -y \ nano \ python3-pip \ python3-venv \ sudo && \ /bin/echo -e ${ISC_PACKAGE_MGRUSER}\\tALL=\(ALL\)\\tNOPASSWD: ALL >> /etc/sudoers && \ sudo -u ${ISC_PACKAGE_MGRUSER} sudo echo enabled passwordless sudo-ing for ${ISC_PACKAGE_MGRUSER} # create dev directory WORKDIR /opt/irisapp RUN chown ${ISC_PACKAGE_MGRUSER}:${ISC_PACKAGE_IRISGROUP} /opt/irisapp USER ${ISC_PACKAGE_MGRUSER} # Copy source files to image COPY . /opt/irisapp # load class test RUN iris start IRIS \ && iris session IRIS < /opt/irisapp/iris.script.test \ && iris stop IRIS quietly # load class supply RUN iris start IRIS \ && iris session IRIS < /opt/irisapp/iris.script.supply \ && iris stop IRIS quietly car ici, vous demarrez deux fois iris dans deux layers differents, vous allez avoir une image plus volumineuse et peut etre des confics aux niveaux des droits dans les bases de données.
accéder à la publication Guillaume Rongier · Oct 11, 2023 Bonjour @Cyril Grosjean, Je ne suis pas sur de comprendre le problème, il me manque peut-etre informations. De ce que j'ai compris : Vous avez deux namespaces : TEST SUPPLY_CHAIN Vous avez une meme classe dans les deux namespaces : nom de la classe : TEST.maclasse Elle a la définition ci-dessous : Class TEST.maclasse Extends %RegisteredObject { ClassMethod testPython() As %Status [ Language = python ] { print("Ok") } } Cette classe est compilée dans les deux namespaces par CI/CD à partir d'un fichier .cls dans git. Le problème : Lorsque vous appelez la méthode testPython() dans le namespace TEST, cela fonctionne. TEST>do ##class(TEST.maclasse).testPython() Ok Lorsque vous appelez la méthode testPython() dans le namespace SUPPLY_CHAIN, cela ne fonctionne pas. SUPPLY_CHAIN>do ##class(TEST.maclasse).testPython() <OBJECT DISPATCH> *python object not found Est-ce que j'ai bien compris ? Si oui : Je n'arrive pas à reproduire le problème. Il y a peut etre un problème avec la CI/CD qui ne compile pas la classe dans le namespace SUPPLY_CHAIN. Il y a peut un problème au niveau de la configuration du namespace SUPPLY_CHAIN. Il me faut plus de détail Si non : Pouvez-vous me donner plus de détail sur le problème ?
accéder à la publication Guillaume Rongier · Oct 10, 2023 Bonjour, Je ne peux pas reproduire votre erreur. Il me manque quelques informations. Ce que j'ai fait jusqu'à présent est : ajouter les imports manquants ajouter la classe DFrameRequest manquante je suppose que c'est une dataclass avec un champ nommé dframe de type pd.DataFrame je suppose que c'est une sous-classe de Message j'ai ajouté une fonction main pour exécuter le code je ne suis pas sûr du format du dataframe et des données qu'il contient from dataclasses import dataclass import pandas as pd from grongier.pex import BusinessOperation,Message from sqlalchemy import create_engine, types @dataclass class DFrameRequest(Message): dframe: pd.DataFrame class FileOperationEmbedded(BusinessOperation): tablename = None engine = None def on_init(self): if not hasattr(self, "dsnIris"): self.dnsIris = 'iris+emb:///' if not hasattr(self, "schema"): self.schema = 'Toto' self.engine = create_engine(self.dnsIris) return None def on_message(self, request:DFrameRequest): df = pd.DataFrame(request.dframe.col) for row in request.dframe.col: df = pd.DataFrame.from_dict(row, orient='index').T.reset_index(drop=True) try: df.to_sql(name=self.tablename, con=self.engine, if_exists='append', index=False, schema=self.schema, dtype={'id': types.INTEGER, 'col_type': types.VARCHAR(50), 'col_center': types.VARCHAR(50), 'col_name': types.VARCHAR(50), 'col_issue_name': types.VARCHAR(50), 'col_model': types.VARCHAR(50), 'col_treatment': types.VARCHAR(50), 'source': types.VARCHAR(50), 'filename': types.VARCHAR(100), 'created_at': types.TIMESTAMP}) except Exception as e: self.log_info(f"Une erreur s'est produite : {e}") return None if __name__ == '__main__': # create a new instance of the business operation bo = FileOperationEmbedded() # initialize the business operation bo.on_init() # create a new message msg = DFrameRequest(pd.DataFrame()) msg.dframe.col = [ {'id': 1, 'col_type': 'type1', 'col_center': 'center1', 'col_name': 'name1', 'col_issue_name': 'issue1', 'col_model': 'model1', 'col_treatment': 'treatment1', 'source': 'source1', 'filename': 'file1', 'created_at': '2021-10-01 00:00:00'}, {'id': 2, 'col_type': 'type2', 'col_center': 'center2', 'col_name': 'name2', 'col_issue_name': 'issue2', 'col_model': 'model2', 'col_treatment': 'treatment2', 'source': 'source2', 'filename': 'file2', 'created_at': '2021-10-02 00:00:00'} ] # send the message to the business operation bo.on_message(msg) print("Done") Ensuite, à partir de votre code, je peux voir les problèmes suivants : vous utilisez le même nom de variable pour le dataframe et la liste des lignes la variable self.tablename n'est pas initialisée le nom FileOperationEmbedded n'est peut-être pas le meilleur nom pour votre classe car ce n'est pas une opération de fichier pourquoi utilisez-vous une boucle for pour itérer sur les lignes du dataframe ? J'ai modifié votre code pour corriger ces problèmes : from dataclasses import dataclass import pandas as pd from grongier.pex import BusinessOperation,Message from sqlalchemy import create_engine, types @dataclass class DFrameRequest(Message): dframe: pd.DataFrame class IrisSqlAlchmyEmbedded(BusinessOperation): tablename = None engine = None def on_init(self): if not hasattr(self, "dsnIris"): self.dnsIris = 'iris+emb:///' if not hasattr(self, "schema"): self.schema = 'Toto' if not hasattr(self, "tablename") or self.tablename is None: self.tablename = 'mytable' self.engine = create_engine(self.dnsIris) return None def on_message(self, request:DFrameRequest): try: request.dframe.to_sql(name=self.tablename, con=self.engine, if_exists='append', index=False, schema=self.schema, dtype={'id': types.INTEGER, 'col_type': types.VARCHAR(50), 'col_center': types.VARCHAR(50), 'col_name': types.VARCHAR(50), 'col_issue_name': types.VARCHAR(50), 'col_model': types.VARCHAR(50), 'col_treatment': types.VARCHAR(50), 'source': types.VARCHAR(50), 'filename': types.VARCHAR(100), 'created_at': types.TIMESTAMP}) except Exception as e: print(f"Une erreur s'est produite : {e}") return None if __name__ == '__main__': # create a new instance of the business operation bo = IrisSqlAlchmyEmbedded() # initialize the business operation bo.on_init() # create a new message msg = DFrameRequest(pd.DataFrame([ {'id': 1, 'col_type': 'type1', 'col_center': 'center1', 'col_name': 'name1', 'col_issue_name': 'issue1', 'col_model': 'model1', 'col_treatment': 'treatment1', 'source': 'source1', 'filename': 'file1', 'created_at': '2021-10-01 00:00:00'}, {'id': 2, 'col_type': 'type2', 'col_center': 'center2', 'col_name': 'name2', 'col_issue_name': 'issue2', 'col_model': 'model2', 'col_treatment': 'treatment2', 'source': 'source2', 'filename': 'file2', 'created_at': '2021-10-02 00:00:00'} ])) # send the message to the business operation bo.on_message(msg) print("Done")
accéder à la publication Guillaume Rongier · Sept 18, 2023 Bonjour, %Net.HttpRequest n'est pas une classe de type message (Ens.Request). Il faut encapsuler le JSON en provenance du Service dans une classe qui herite de Ens.Request.
accéder à la publication Guillaume Rongier · Déc 12, 2022 Les versions client d'IRIS/ HealthShare sont fournis avec l'installation du Serveur dans le dossier suivant : <dossier d'install>/dev example sous linux : /usr/irissys/dev/dotnet/ `-- bin |-- InterSystems.Data.Bindings.2.0.0.nupkg |-- InterSystems.Data.IRISClient.2.0.0.nupkg |-- InterSystems.Data.Utils.2.0.0.nupkg |-- InterSystems.Data.XEP.2.0.0.nupkg |-- net5.0 | |-- InterSystems.Data.Gateway.pdb | |-- InterSystems.Data.Gateway.xml | |-- InterSystems.Data.GatewayArm | |-- InterSystems.Data.GatewayLinux | |-- InterSystems.Data.GatewayOSX | |-- InterSystems.Data.IRISClient.dll | |-- InterSystems.Data.IRISClient.xml | |-- InterSystems.Data.Utils.dll | |-- InterSystems.Data.Utils.xml | `-- Newtonsoft.Json.dll `-- net6.0 |-- InterSystems.Data.Gateway.pdb |-- InterSystems.Data.Gateway.xml |-- InterSystems.Data.GatewayArm |-- InterSystems.Data.GatewayLinux |-- InterSystems.Data.GatewayOSX |-- InterSystems.Data.IRISClient.dll |-- InterSystems.Data.IRISClient.xml |-- InterSystems.Data.Utils.dll |-- InterSystems.Data.Utils.xml `-- Newtonsoft.Json.dll Toutes ces libraires peuvent etre utilisées sur une autre machine, elles ouvreront une socket TCP pour effectuer la connection sur le superserver port (1972 par default, 51773 pour HealthShare, enfin je crois).
accéder à la publication Guillaume Rongier · Nov 14, 2022 Bonjour Alexandre, Tu trouveras sur ce git, la meme version que celle de lucas mais avec un docker-compose et un dockerfile mis à jour pour installer automatiquement la partie front. https://github.com/grongierisc/fhir-form Je n'ai pas eu de problème de dépendance avec node 14. Ton erreur vient peut etre du fait que tu es en node 16.
accéder à la publication Guillaume Rongier · Nov 10, 2022 @Lucas Enard Peux tu nous aider sur cette question ?
accéder à la publication Guillaume Rongier · Août 2, 2022 Super article, j'adore, il parle aussi bien à des personnes techniques que de culture générale sur les bases de données (SQL, NoSQL, stockage colonne, stockage en ligne). Bref, un must read !
accéder à la publication Guillaume Rongier · Juin 21, 2022 C'est un plaisir de partager ce moment avec vous !
accéder à la publication Guillaume Rongier · Avr 11, 2022 Article important, à partir de docker desktop 4.7.0 Cette erreur ce produit.
accéder à la publication Guillaume Rongier · Avr 1, 2022 Bonjour, Je vous envoie mes coordonnées par messages privées, nous pourrons discuter de votre besoin sur un autre canal (mail par exemple). Cordialement, Guillaume
accéder à la publication Guillaume Rongier · Mars 23, 2022 Salut Fabien, Nous n'allons pas traduire la documentation. Mais tu vas voir de plus en plus d'article anglais traduit en français.