1- import asyncio
2- import uuid
3- from os import environ
41from typing import Optional
52from uuid import UUID , uuid4
63
7- from prefect import flow , get_client , task
8- from prefect . blocks . system import Secret
9- from pydantic .v1 import Field
4+ from opensemantic . base . v1 import Article
5+ from prefect import flow , task
6+ from pydantic .v1 import BaseModel , Field
107
118import osw .model .entity as model
12- from osw .auth import CredentialManager
139from osw .core import OSW
14- from osw .utils .wiki import get_full_title
10+ from osw .utils .workflow import (
11+ ConnectionSettings ,
12+ DeployConfig ,
13+ DeployParam ,
14+ WorkflowRequest ,
15+ connect ,
16+ deploy ,
17+ )
1518from osw .wtsite import WtSite
1619
17-
18- class ConnectionSettings (model .OswBaseModel ):
19- """Connection data for OSW"""
20-
21- osw_user_name : Optional [str ]
22- """The login username.
23- Note: value of envar OSW_USER used of not given
24- Note: value of envar OSW_PASSWORD used for login"""
25- osw_domain : Optional [str ]
26- """The domain of the instance
27- Note: value of envar OSW_SERVER used of not given"""
20+ # Module-level OSW instance, set by the connect_osw task
21+ osw : Optional [OSW ] = None
2822
2923
3024@task
31- def connect (settings : Optional [ConnectionSettings ] = None ):
32- """Initiates the connection to the OSW instance
33-
34- Parameters
35- ----------
36- settings
37- see ConnectionSetttings dataclass
38- """
39- if settings is None :
40- settings = ConnectionSettings ()
41- global wtsite
42- # define username
43- if environ .get ("OSW_USER" ) is not None and environ .get ("OSW_USER" ) != "" :
44- settings .osw_user_name = environ .get ("OSW_USER" )
45- if environ .get ("OSW_SERVER" ) is not None and environ .get ("OSW_SERVER" ) != "" :
46- settings .osw_domain = environ .get ("OSW_SERVER" )
47- password = ""
48- if environ .get ("OSW_PASSWORD" ) is not None and environ .get ("OSW_PASSWORD" ) != "" :
49- password = environ .get ("OSW_PASSWORD" )
50- else :
51- # fetch secret stored in prefect server from calculated name
52- password = Secret .load (
53- settings .osw_user_name .lower () + "-" + settings .osw_domain .replace ("." , "-" )
54- ).get () # e. g. mybot-wiki-dev-open-semantic-lab-org
55- cm = CredentialManager ()
56- cm .add_credential (
57- CredentialManager .UserPwdCredential (
58- iri = settings .osw_domain , username = settings .osw_user_name , password = password
59- )
60- )
61- wtsite = WtSite (WtSite .WtSiteConfig (iri = settings .osw_domain , cred_mngr = cm ))
25+ def connect_osw (settings : Optional [ConnectionSettings ] = None ):
26+ """Initiates the connection to the OSW instance"""
6227 global osw
63- osw = OSW ( site = wtsite )
28+ osw = connect ( settings )
6429
6530
6631@task
6732def fetch_schema ():
68- """this will load the current entity schema from the OSW instance."""
69- # Load Article Schema on demand
70- if not hasattr (model , "Article" ):
71- osw .fetch_schema (
72- OSW .FetchSchemaParam (
73- schema_title = [
74- "Category:OSW77e749fc598341ac8b6d2fff21574058" , # Software
75- "Category:OSW72eae3c8f41f4a22a94dbc01974ed404" , # PrefectFlow
76- "Category:OSW92cc6b1a2e6b4bb7bad470dfdcfdaf26" , # Article
77- ],
78- mode = "replace" ,
79- )
80- )
33+ """Fetch custom schemas not yet available in packages.
34+
35+ Software, PrefectFlow, and Article are already provided by
36+ opensemantic.base, so no fetch is needed for this example.
37+ Uncomment and adapt the code below if your workflow uses
38+ schemas that are only available on the OSW instance.
39+ """
40+ # osw.fetch_schema(
41+ # OSW.FetchSchemaParam(
42+ # schema_title=[
43+ # "Category:OSW...", # your custom category
44+ # ],
45+ # mode="replace",
46+ # )
47+ # )
8148
8249
83- class Result (model . OswBaseModel ):
50+ class Result (BaseModel ):
8451 """The result dataclass"""
8552
8653 uuid : Optional [UUID ] = Field (default_factory = uuid4 , title = "UUID" )
@@ -106,15 +73,21 @@ def store_and_document_result(result: Result):
10673 title = result .target_title
10774 else :
10875 title = "Item:" + osw .get_osw_id (result .uuid )
109- entity = osw .load_entity (title )
76+ entity = osw .load_entity (
77+ OSW .LoadEntityParam (
78+ titles = [title ],
79+ autofetch_schema = False ,
80+ model_to_use = Article ,
81+ )
82+ ).entities [0 ]
11083 if entity is None :
11184 # does not exist yet - create a new one
112- entity = model . Article (
85+ entity = Article (
11386 uuid = result .uuid , label = [model .Label (text = "Article for dummy workflow" )]
11487 )
11588
11689 # edit structured data
117- entity = entity .cast (model . Article )
90+ entity = entity .cast (Article )
11891 entity .description = [model .Description (text = "some descriptive text" )]
11992 osw .store_entity (entity )
12093
@@ -127,13 +100,9 @@ def store_and_document_result(result: Result):
127100 print ("FINISHED" )
128101
129102
130- class Request (model .OswBaseModel ):
131- uuid : UUID = Field (default_factory = uuid4 , title = "UUID" )
132- """UUIDv4 of the request."""
133- osw_domain : Optional [str ] = "wiki-dev.open-semantic-lab.org"
134- """To domain of the OSW instance"""
135- subject : Optional [str ] = "Item:OSW56f9439d43244fe7a83163bab9414ee1"
136- """Where to store the results. For testing, we use a static default value"""
103+ class Request (WorkflowRequest ):
104+ """Request for the dummy workflow."""
105+
137106 msg : Optional [str ] = "test message"
138107 """The message you want to leave on the target page"""
139108
@@ -150,56 +119,24 @@ def dummy_workflow(request: Request):
150119 request
151120 see Request dataclass
152121 """
153- connect (ConnectionSettings (osw_domain = request .osw_domain ))
122+ connect_osw (ConnectionSettings (osw_domain = request .osw_domain ))
154123 fetch_schema ()
155124 store_and_document_result (Result (msg = request .msg , target_title = request .subject ))
156125
157126
158- async def deploy ():
159- """programmatic deployment supported in newer prefect versions"""
160- flow = dummy_workflow
161- # flow_name = flow.name
162- deployment_name = flow .name + " Deployment"
163-
164- # create a deployment and apply it
165- config = await flow .to_deployment (name = deployment_name )
166- await config .apply () # returns the deployment_uuid
167-
168- # fetch flow uuid
169- async with get_client () as client :
170- response = await client .read_flow_by_name (flow .name )
171- print (response .json ())
172- flow_uuid = response .id
173-
174- await connect ()
175- await fetch_schema ()
176- # static UUIDv5 namespace for a stable UUID
177- namespace_uuid = uuid .UUID ("0dd6c54a-b162-4552-bab9-9942ccaf4f41" )
178-
179- # self-documentation / registration
180- this_tool = model .Software (
181- uuid = uuid .uuid5 (namespace_uuid , flow .name ),
182- label = [model .Label (text = flow .name )],
183- description = [model .Description (text = flow .description )],
184- )
185-
186- prefect_domain = environ .get ("PREFECT_API_URL" ).split ("//" )[- 1 ].split ("/" )[0 ]
187- this_flow = model .PrefectFlow (
188- uuid = flow_uuid ,
189- label = [model .Label (text = flow .name + " Prefect Flow" )],
190- description = [model .Description (text = flow .description )],
191- flow_id = str (flow_uuid ),
192- hosted_software = [get_full_title (this_tool )],
193- domain = prefect_domain ,
194- )
195-
196- osw .store_entity (osw .StoreEntityParam (entities = [this_tool , this_flow ]))
197-
198- # start agent to serve deployment
199- await dummy_workflow .serve (name = deployment_name )
200-
201-
202127if __name__ == "__main__" :
203- # dummy_workflow(Request(msg="Test"))
204- with asyncio .Runner () as runner :
205- runner .run (deploy ())
128+ # Direct run: dummy_workflow(Request(msg="Test"))
129+
130+ # Deploy and serve with OSW registration
131+ osw_instance = connect ()
132+ deploy (
133+ DeployParam (
134+ deployments = [
135+ DeployConfig (
136+ flow = dummy_workflow ,
137+ name = "Dummy Workflow Deployment" ,
138+ )
139+ ],
140+ osw = osw_instance ,
141+ )
142+ )
0 commit comments