cloudpss 4.0.2a1__py3-none-any.whl → 4.1.1a1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
cloudpss/model/model.py CHANGED
@@ -1,52 +1,58 @@
1
+ import asyncio
1
2
  import os
2
3
  import re
3
- import io
4
- import json
5
- import yaml
6
- import gzip
7
4
  from copy import deepcopy
8
5
 
6
+ from cloudpss.job.job import Job
7
+ from cloudpss.utils.IO import IO
8
+ from cloudpss.utils.httpAsyncRequest import graphql_fetch
9
+
9
10
  from .revision import ModelRevision
10
11
  from .jobDefinitions import JOB_DEFINITIONS
11
- from ..utils import request, fileLoad, graphql_request
12
12
  from ..verify import userName
13
13
 
14
- from cloudpss.runner.result import PowerFlowResult, EMTResult, Result, IESResult
15
- from cloudpss.runner.runner import Runner
14
+ from cloudpss.runner.result import IESResult
15
+ from cloudpss.runner.runner import Runner
16
16
 
17
17
 
18
18
  class Model(object):
19
19
  """
20
- CloudPSS工程类,用于处理加载后的工程数据
20
+ CloudPSS工程类,用于处理加载后的工程数据
21
21
 
22
- 实例变量说明:
22
+ 实例变量说明:
23
23
 
24
- rid 项目的 rid
24
+ rid 项目的 rid
25
25
 
26
- name 项目的名称
26
+ name 项目的名称
27
27
 
28
- description 项目的描述
28
+ description 项目的描述
29
29
 
30
- revision 当前项目的版本信息
30
+ revision 当前项目的版本信息
31
31
 
32
- configs 当前项目的所有参数方案
32
+ configs 当前项目的所有参数方案
33
33
 
34
- jobs 当前项目的所有计算方案
34
+ jobs 当前项目的所有计算方案
35
35
 
36
- context 当前项目的上下文相关信息
36
+ context 当前项目的上下文相关信息
37
37
 
38
38
  """
39
+
39
40
  context: dict
40
41
  jobs: list
41
42
  configs: list
42
43
 
43
44
  def __init__(self, model: dict = {}):
44
45
  """
45
- 项目初始化
46
+ 项目初始化
46
47
  """
47
48
  for k, v in model.items():
48
- if k == 'revision':
49
- self.revision = ModelRevision(v)
49
+ if k == "revision":
50
+ if "version" in v and v["version"] < 5:
51
+ self.revision = ModelRevision(v)
52
+ else:
53
+ raise Exception(
54
+ "当前SDK版本(ver 3.X.X)不兼容该项目文件,请先升级项目文件。具体方法:将该项目文件导入至XStudio 3.X.X平台后重新保存至本地后即可。"
55
+ )
50
56
 
51
57
  else:
52
58
  self.__dict__[k] = v
@@ -56,82 +62,42 @@ class Model(object):
56
62
 
57
63
  def toJSON(self):
58
64
  """
59
- 类对象序列化为 dict
60
- :return: dict
65
+ 类对象序列化为 dict
66
+ :return: dict
61
67
  """
62
- model = {**self.__dict__, 'revision': self.revision.toJSON()}
68
+ model = {**self.__dict__, "revision": self.revision.toJSON()}
63
69
  return model
64
70
 
65
71
  def getAllComponents(self):
66
72
  """
67
- 获取实现
68
-
69
- :return: 所有元件信息
70
-
71
- >>> model.getAllComponents()
72
- {
73
- 'canvas_0_2': Component 实例
74
- }
75
- """
76
- diagramImplement = self.revision.getImplements().getDiagram()
77
- if diagramImplement is None:
78
- raise ValueError('不存在拓扑实现')
79
- return diagramImplement.getAllComponents()
80
-
81
- def addComponent(self, definition, label, args, pins, canvas=None, position=None, size=None):
82
- """
83
- 添加元件实现
73
+ 获取实现
84
74
 
85
- :return: Component
75
+ :return: 所有元件信息
86
76
 
87
- >>>> model.addComponent(args)
88
- """
89
- diagramImplement = self.revision.getImplements().getDiagram()
90
- if diagramImplement is None:
91
- raise ValueError("不存在拓扑实现")
92
- return diagramImplement.addComponent(definition, label, args, pins, canvas, position, size)
93
-
94
- def removeComponent(self, key):
95
- """
96
- 删除元件实现
97
-
98
- :return: boolean
99
-
100
- >>>> model.removeComponent(key)
101
- """
102
- diagramImplement = self.revision.getImplements().getDiagram()
103
- if diagramImplement is None:
104
- raise ValueError("不存在拓扑实现")
105
- return diagramImplement.removeComponent(key)
106
-
107
- def updateComponent(self, key, args):
108
- """
109
- 更新元件实现
110
-
111
- :return: bool
112
-
113
- >>>> model.updateComponent(key, args)
77
+ >>> model.getAllComponents()
78
+ {
79
+ 'canvas_0_2': Component 实例
80
+ }
114
81
  """
115
82
  diagramImplement = self.revision.getImplements().getDiagram()
116
83
  if diagramImplement is None:
117
84
  raise ValueError("不存在拓扑实现")
118
- return diagramImplement.updateComponent(key, args)
119
-
85
+ return diagramImplement.getAllComponents()
120
86
 
121
87
  def getComponentsByRid(self, rid: str):
122
88
  """
123
- 通过指定元件类型获取元件
89
+ 通过指定元件类型获取元件
124
90
 
125
- :params str: 元件类型
91
+ :params str: 元件类型
126
92
 
127
- :type rid: str
93
+ :type rid: str
128
94
 
129
- :return: 按照元件的 rid 过滤后的 dict<>
95
+ :return: 按照元件的 rid 过滤后的 dict<>
130
96
 
131
- >>> model.getComponentsByRid('model/CloudPSS/newInductorRouter')
132
- {
133
- 'canvas_0_2': Component 实例
134
- }
97
+ >>> model.getComponentsByRid('model/CloudPSS/newInductorRouter')
98
+ {
99
+ 'canvas_0_2': Component 实例
100
+ }
135
101
 
136
102
  """
137
103
 
@@ -139,8 +105,7 @@ class Model(object):
139
105
 
140
106
  cells = {}
141
107
  for key, val in v.items():
142
-
143
- if not val.shape.startswith('diagram-component'):
108
+ if not val.shape.startswith("diagram-component"):
144
109
  continue
145
110
  if val.definition == rid:
146
111
  cells[key] = val
@@ -148,14 +113,14 @@ class Model(object):
148
113
 
149
114
  def getComponentByKey(self, componentKey: str):
150
115
  """
151
- 通过元件的 key 获取对应的元件
116
+ 通过元件的 key 获取对应的元件
152
117
 
153
- :params key: key 元件的key
118
+ :params key: key 元件的key
154
119
 
155
- :Return: Component 实例
120
+ :Return: Component 实例
156
121
 
157
- >>> model.getComponentByKey('canvas_0_757')
158
- Component 实例
122
+ >>> model.getComponentByKey('canvas_0_757')
123
+ Component 实例
159
124
  """
160
125
 
161
126
  v = self.getAllComponents()
@@ -163,144 +128,158 @@ class Model(object):
163
128
 
164
129
  def getModelJob(self, name):
165
130
  """
166
- 获取指定名称的计算方案
131
+ 获取指定名称的计算方案
167
132
 
168
- :params Name: Name 参数名称
133
+ :params Name: Name 参数名称
169
134
 
170
- :return: 同名计算方案数组
135
+ :return: 同名计算方案数组
171
136
 
172
- >>> model.getModelJob('电磁暂态方案 1')
137
+ >>> model.getModelJob('电磁暂态方案 1')
173
138
  """
174
139
  jobs = []
175
140
 
176
141
  for val in self.jobs:
177
- if val['name'] == name:
142
+ if val["name"] == name:
178
143
  jobs.append(val)
179
144
 
180
145
  return jobs
181
146
 
182
147
  def createJob(self, jobType: str, name):
183
148
  """
184
- 创建一个计算方案
185
- 创建出的方案默认不加入到项目中,需要加入请调用 addJob
149
+ 创建一个计算方案
150
+ 创建出的方案默认不加入到项目中,需要加入请调用 addJob
186
151
 
187
- :params jobType: 方案类型
188
- 电磁暂态仿真方案 emtp
189
- 移频电磁暂态仿真方案 sfemt
190
- 潮流计算方案 powerFlow
152
+ :params jobType: 方案类型
153
+ 电磁暂态仿真方案 emtp
154
+ 移频电磁暂态仿真方案 sfemt
155
+ 潮流计算方案 powerFlow
191
156
 
192
- :return: 返回一个指定类型的计算方案
157
+ :return: 返回一个指定类型的计算方案
193
158
 
194
- >>> model.createJob('emtp','emtp job')
195
- 计算方案
159
+ >>> model.createJob('emtp','emtp job')
160
+ 计算方案
196
161
  """
197
162
  job = deepcopy(JOB_DEFINITIONS[jobType])
198
- job['name'] = name
163
+ job["name"] = name
199
164
  return job
200
165
 
201
166
  def addJob(self, job: dict):
202
167
  """
203
- 将计算方案添加到工程中
168
+ 将计算方案添加到工程中
204
169
 
205
- :params job: 计算方案 dict
170
+ :params job: 计算方案 dict
206
171
 
207
- >>> job = model.createJob('emtp','emtp job')
208
- model.addJob(job)
172
+ >>> job = model.createJob('emtp','emtp job')
173
+ model.addJob(job)
209
174
  """
210
175
 
211
176
  self.jobs.append(job)
212
177
 
213
178
  def getModelConfig(self, name):
214
179
  """
215
- 获取指定名称的参数方案
180
+ 获取指定名称的参数方案
216
181
 
217
- :params name: 参数方案名称
182
+ :params name: 参数方案名称
218
183
 
219
- :return: 同名的方案数组
184
+ :return: 同名的方案数组
220
185
 
221
- >>> model.getModelConfig('参数方案 1')
186
+ >>> model.getModelConfig('参数方案 1')
222
187
  """
223
188
  configs = []
224
189
 
225
190
  for val in self.configs:
226
- if val['name'] == name:
191
+ if val["name"] == name:
227
192
  configs.append(val)
228
193
 
229
194
  return configs
230
195
 
231
196
  def createConfig(self, name):
232
197
  """
233
- 创建一个参数方案
234
- 根据项目的第一个参数方案生成一个方案
235
- 创建出的方案默认不加入到项目中,需要加入请调用 addConfig
236
- :params name: 参数方案名称
198
+ 创建一个参数方案
199
+ 根据项目的第一个参数方案生成一个方案
200
+ 创建出的方案默认不加入到项目中,需要加入请调用 addConfig
201
+ :params name: 参数方案名称
237
202
 
238
- :return: 返回一个参数方案 dict
203
+ :return: 返回一个参数方案 dict
239
204
 
240
- >>> job = model.createConfig('my config')
241
- 参数方案
205
+ >>> job = model.createConfig('my config')
206
+ 参数方案
242
207
  """
243
208
 
244
209
  config = deepcopy(self.configs[0])
245
- config['name'] = name
210
+ config["name"] = name
246
211
  return config
247
212
 
248
213
  def addConfig(self, config):
249
214
  """
250
- 将参数方案添加到工程中
215
+ 将参数方案添加到工程中
251
216
 
252
- :params config: 参数方案 dict
217
+ :params config: 参数方案 dict
253
218
 
254
- >>> config = model.createConfig('my config')
255
- model.addConfig(config)
219
+ >>> config = model.createConfig('my config')
220
+ model.addConfig(config)
256
221
  """
257
222
 
258
223
  self.configs.append(config)
259
224
  return config
260
225
 
261
226
  @staticmethod
262
- def fetchMany(name=None, pageSize=10, pageOffset=0):
227
+ def fetchMany(name=None, cursor=[]):
228
+ return asyncio.run(Model.fetchManyAsync(name, cursor))
229
+
230
+ @staticmethod
231
+ async def fetchManyAsync(name=None, cursor=[]):
263
232
  """
264
- 获取用户可以运行的项目列表
233
+ 获取用户可以运行的项目列表
265
234
 
266
- :params name: 查询名称,模糊查询
267
- :params pageSize: 分页大小
268
- :params pageOffset: 分页开始位置
235
+ :params name: 查询名称,模糊查询
236
+ :params cursor: 游标
269
237
 
270
- :return: 按分页信息返回项目列表
238
+ :return: 按分页信息返回项目列表
271
239
 
272
- >>> data=Model.fetchMany()
273
- [
240
+ >>> data= await Model.fetchManyAsync()
241
+ {
242
+ items: [
274
243
  {'rid': 'model/admin/share-test', 'name': '1234', 'description': '1234'}
275
244
  ...
276
- ]
245
+ ],
246
+ cursor: ["1699353593000"],
247
+
248
+ }
249
+
250
+
277
251
  """
252
+ query = """query($input:ModelsInput!){models(input:$input){cursor total count items{rid name description owner tags updatedAt pages{pageId resource key path mode}access{permission role}}}}"""
253
+ variables = {
254
+ "cursor": cursor,
255
+ "limit": 10,
256
+ "orderBy": ["updatedAt<"],
257
+ "permissionEveryone": ["b_any", 2**16],
258
+ }
259
+ if name is not None:
260
+ variables["_search"] = name
278
261
 
279
- r = request('GET',
280
- 'api/resources/type/model/permission/write',
281
- params={
282
- 'page_offset': pageOffset,
283
- 'page_size': pageSize,
284
- 'search': name
285
- })
286
- modelList = json.loads(r.text)
262
+ data = await graphql_fetch(query, {"input": variables})
287
263
 
288
- return [{
289
- 'rid': '/'.join([val['type'], val['owner'], val['key']]),
290
- 'name': val['name'],
291
- 'description': val['description']
292
- } for val in modelList]
264
+ if "errors" in data:
265
+ raise Exception(data["errors"][0]["message"])
266
+
267
+ return data["data"]["models"]
293
268
 
294
269
  @staticmethod
295
270
  def fetch(rid):
271
+ return asyncio.run(Model.fetchAsync(rid))
272
+
273
+ @staticmethod
274
+ async def fetchAsync(rid):
296
275
  """
297
- 获取项目
276
+ 获取项目
298
277
 
299
- :params rid: 项目 rid
278
+ :params rid: 项目 rid
300
279
 
301
- :return: 返回一个项目实例
280
+ :return: 返回一个项目实例
302
281
 
303
- >>> model=Model.fetch('model/Demo/test')
282
+ >>> model=Model.fetch('model/Demo/test')
304
283
 
305
284
  """
306
285
  query = """
@@ -313,6 +292,12 @@ class Model(object):
313
292
  name
314
293
  rid
315
294
  tags
295
+ keywords
296
+ permissions{
297
+ member
298
+ moderator
299
+ everyone
300
+ }
316
301
  revision {
317
302
  author
318
303
  documentation
@@ -327,229 +312,247 @@ class Model(object):
327
312
  }
328
313
  }
329
314
  """
330
- data = graphql_request(query, {'rid': rid})
331
- if 'errors' in data:
332
- raise Exception(data['errors'][0]['message'])
333
- return Model(data['data']['model'])
315
+ data = await graphql_fetch(query, {"rid": rid})
316
+ if "errors" in data:
317
+ raise Exception(data["errors"][0]["message"])
318
+ return Model(data["data"]["model"])
319
+
320
+ def run(self, job=None, config=None, name=None, policy=None, **kwargs):
321
+ return asyncio.run(self.runAsync(job, config, name, policy, **kwargs))
334
322
 
335
- def run(self, job=None, config=None, name=None, **kwargs):
323
+ async def runAsync(self, job=None, config=None, name=None, policy=None, **kwargs):
336
324
  """
337
325
 
338
- 调用仿真
326
+ 调用仿真
339
327
 
340
- :params job: 调用仿真时使用的计算方案,不指定将使用算例保存时选中的计算方案
341
- :params config: 调用仿真时使用的参数方案,不指定将使用算例保存时选中的参数方案
342
- :params name: 任务名称,为空时使用项目的参数方案名称和计算方案名称
328
+ :params job: 调用仿真时使用的计算方案,不指定将使用算例保存时选中的计算方案
329
+ :params config: 调用仿真时使用的参数方案,不指定将使用算例保存时选中的参数方案
330
+ :params name: 任务名称,为空时使用项目的参数方案名称和计算方案名称
343
331
 
344
- :return: 返回一个运行实例
332
+ :return: 返回一个Job实例
345
333
 
346
- >>> runner=model.run(job,config,'')
347
- runner
334
+ >>> job=model.run(job,config,'')
335
+ job
348
336
 
349
337
  """
350
338
  if job is None:
351
- currentJob = self.context['currentJob']
339
+ currentJob = self.context["currentJob"]
352
340
  job = self.jobs[currentJob]
353
341
  if config is None:
354
- currentConfig = self.context['currentConfig']
342
+ currentConfig = self.context["currentConfig"]
355
343
  config = self.configs[currentConfig]
356
- return self.revision.run(job, config, name, self.rid, **kwargs)
344
+ revision = await self.revision.run(
345
+ job, config, name, self.rid, policy, **kwargs
346
+ )
347
+ return await Job.create(
348
+ revision["hash"], job, config, name, self.rid, policy, **kwargs
349
+ )
357
350
 
358
351
  def iesSimulationRun(self, job=None, config=None, name=None, **kwargs):
359
352
  return self.run(job=job, config=config, name=name, kwargs=kwargs)
360
353
 
361
354
  @staticmethod
362
- def load(filePath):
355
+ def load(filePath, format="yaml"):
363
356
  """
364
- 加载本地项目文件
357
+ 加载本地项目文件
365
358
 
366
- :params file: 文件目录
359
+ :params file: 文件目录
367
360
 
368
- :return: 返回一个项目实例
361
+ :format 默认格式为yaml
369
362
 
370
- >>> model = Model.load('filePath')
363
+ :return: 返回一个项目实例
364
+
365
+ >>> model = Model.load('filePath')
371
366
 
372
367
  """
373
368
 
374
369
  if not os.path.exists(filePath):
375
- raise FileNotFoundError('未找到文件')
376
- data = fileLoad(filePath)
370
+ raise FileNotFoundError("未找到文件")
371
+ data = IO.load(filePath, format)
377
372
  return Model(data)
378
373
 
379
374
  @staticmethod
380
- def dump(model, file):
375
+ def dump(model, file, format="yaml", compress="gzip"):
381
376
  """
382
- 下载项目文件
377
+ 下载项目文件
383
378
 
384
- :params model: 项目
385
- :params file: 文件路径
379
+ :params model: 项目
380
+ :params file: 文件路径
381
+ :params format: 文件格式
382
+ :params compress: 是否压缩 压缩格式目前支持gzip 为None时不压缩
386
383
 
387
- :return: 无
384
+ :return: 无
388
385
 
389
- >>> Model.dump(model,file)
386
+ >>> Model.dump(model,file)
390
387
  """
391
388
 
392
- data = model.toJSON()
393
-
394
- yamlData = yaml.dump(data)
395
- with gzip.open(file, 'w') as output:
396
- with io.TextIOWrapper(output, encoding='utf-8') as enc:
397
- enc.write(yamlData)
389
+ IO.dump(model, file, format, compress)
398
390
 
399
391
  def save(self, key=None):
392
+ return asyncio.run(self.saveAsync(key))
393
+
394
+ async def saveAsync(self, key=None):
400
395
  """
401
- 保存/创建项目
396
+ 保存/创建项目
402
397
 
403
- key 不为空时如果远程存在相同的资源名称时将覆盖远程项目。
404
- key 为空时如果项目 rid 不存在则抛异常,需要重新设置 key。
405
- 如果保存时,当前用户不是该项目的拥有者时,将重新创建项目,重建项目时如果参数的 key 为空将使用当前当前项目的 key 作为资源的 key ,当资源的 key 和远程冲突时保存失败
398
+ key 不为空时如果远程存在相同的资源名称时将覆盖远程项目。
399
+ key 为空时如果项目 rid 不存在则抛异常,需要重新设置 key。
400
+ 如果保存时,当前用户不是该项目的拥有者时,将重新创建项目,重建项目时如果参数的 key 为空将使用当前当前项目的 key 作为资源的 key ,当资源的 key 和远程冲突时保存失败
406
401
 
407
- :params: model 项目
408
- :params: key 资源 id 的唯一标识符,
409
-
410
- :return: 保存成功/保存失败
411
-
412
- >>> model.save(model)
413
- model.save(model,'newKey') # 另存为新的项目
402
+ :params: model 项目
403
+ :params: key 资源 id 的唯一标识符,
404
+
405
+ :return: 保存成功/保存失败
406
+
407
+ >>> model.save(model)
408
+ model.save(model,'newKey') # 另存为新的项目
414
409
 
415
410
  """
416
411
  username = userName()
417
412
 
418
413
  if key is not None:
419
- matchObj = re.match(r'^[-_A-Za-z0-9]+$', key, re.I | re.S)
414
+ matchObj = re.match(r"^[-_A-Za-z0-9]+$", key, re.I | re.S)
420
415
  if matchObj:
421
- self.rid = 'model/' + username + '/' + key
416
+ self.rid = "model/" + username + "/" + key
422
417
  try:
423
- r = request('GET', 'api/resources/' + self.rid)
424
- return Model.update(self)
418
+ return await Model.updateAsync(self)
425
419
  except:
426
- return Model.create(self)
420
+ return await Model.createAsync(self)
427
421
  else:
428
- raise Exception('key 能包含字母数子和下划线')
422
+ raise Exception("key 能包含字母数子和下划线")
429
423
  else:
430
- t = '(?<=/)\\S+(?=/)'
424
+ t = "(?<=/)\\S+(?=/)"
431
425
  owner = re.search(t, self.rid)
432
426
  if owner is None:
433
- raise Exception('rid 错误,请传入 key')
427
+ raise Exception("rid 错误,请传入 key")
434
428
  elif owner[0] != username:
435
429
  rid = re.sub(t, username, self.rid)
436
430
  try:
437
- r = request('GET', 'api/resources/' + self.rid)
438
- return Model.create(self)
431
+ return await Model.createAsync(self)
439
432
  except:
440
- raise Exception(rid + ' 该资源已存在,无法重复创建,请修改 key')
433
+ raise Exception(rid + " 该资源已存在,无法重复创建,请修改 key")
441
434
 
442
- return Model.update(self)
435
+ return await Model.updateAsync(self)
443
436
 
444
437
  @staticmethod
445
438
  def create(model):
439
+ return asyncio.run(Model.createAsync(model))
440
+
441
+ @staticmethod
442
+ async def createAsync(model):
446
443
  """
447
- 新建项目
444
+ 新建项目
448
445
 
449
- :params: model 项目
446
+ :params: model 项目
450
447
 
451
- :return: 保存成功/保存失败
448
+ :return: 保存成功/保存失败
452
449
 
453
- >>> Model.create(model)
454
- 保存成功
450
+ >>> Model.create(model)
451
+ 保存成功
455
452
  """
456
453
  # Model.update(model)
457
- t = '(?<=/)\\S+(?=/)'
454
+ t = "(?<=/)\\S+(?=/)"
458
455
  username = userName()
459
456
  owner = re.search(t, model.rid)
460
457
 
461
458
  if owner is None:
462
- raise Exception('rid 错误,无法保存')
459
+ raise Exception("rid 错误,无法保存")
463
460
  elif owner[0] != username:
464
- raise Exception('rid 错误,无法保存')
461
+ raise Exception("rid 错误,无法保存")
465
462
 
466
463
  modelQuery = """
467
464
  mutation($a:CreateModelInput!){createModel(input:$a){
468
465
  rid
469
466
  }}
470
467
  """
471
- isPublic = model.context.get('auth', '') != 'private'
472
- isComponent = model.context.get('category', '') == 'component'
473
- publicRead = model.context.get('publicRead', '') != False
468
+ isPublic = model.context.get("auth", "") != "private"
469
+ isComponent = model.context.get("category", "") == "component"
470
+ publicRead = model.context.get("publicRead", "") != False
474
471
  auth = (65539 if publicRead else 65537) if isPublic else 0
475
- revision = ModelRevision.create(model.revision, model.revision.hash)
476
-
477
- return graphql_request(
478
- modelQuery, {
479
- 'a': {
480
- 'rid': model.rid,
481
- 'revision': revision['hash'],
482
- 'context': model.context,
483
- 'configs': model.configs,
484
- 'jobs': model.jobs,
485
- 'name': model.name,
486
- 'description': model.description,
487
- 'tags': model.tags,
472
+ revision = await ModelRevision.create(model.revision, model.revision.hash)
473
+
474
+ return await graphql_fetch(
475
+ modelQuery,
476
+ {
477
+ "a": {
478
+ "rid": model.rid,
479
+ "revision": revision["hash"],
480
+ "context": model.context,
481
+ "configs": model.configs,
482
+ "jobs": model.jobs,
483
+ "name": model.name,
484
+ "description": model.description,
485
+ "tags": model.tags,
488
486
  "permissions": {
489
487
  "moderator": 1,
490
488
  "member": 1,
491
489
  "everyone": auth,
492
490
  },
493
491
  }
494
- })
492
+ },
493
+ )
495
494
 
496
495
  @staticmethod
497
496
  def update(model):
497
+ return asyncio.run(Model.updateAsync(model))
498
+
499
+ @staticmethod
500
+ async def updateAsync(model):
498
501
  """
499
- 更新项目
502
+ 更新项目
500
503
 
501
- :params: model 项目
504
+ :params: model 项目
502
505
 
503
- :return: 保存成功/保存失败
506
+ :return: 保存成功/保存失败
504
507
 
505
- >>> Model.update(model)
508
+ >>> Model.update(model)
506
509
  """
507
510
 
508
- t = '(?<=/)\\S+(?=/)'
511
+ t = "(?<=/)\\S+(?=/)"
509
512
  username = userName()
510
513
  owner = re.search(t, model.rid)
511
514
 
512
515
  if owner is None:
513
- raise Exception('rid 错误,无法保存')
516
+ raise Exception("rid 错误,无法保存")
514
517
  elif owner[0] != username:
515
- raise Exception('rid 错误,无法保存')
518
+ raise Exception("rid 错误,无法保存")
516
519
 
517
520
  modelQuery = """
518
521
  mutation($a:UpdateModelInput!){updateModel(input:$a){
519
522
  rid
520
523
  }}
521
524
  """
522
- isPublic = model.context.get('auth', '') != 'private'
523
- isComponent = model.context.get('category', '') == 'component'
524
- publicRead = model.context.get('publicRead', '') != False
525
+ isPublic = model.context.get("auth", "") != "private"
526
+ isComponent = model.context.get("category", "") == "component"
527
+ publicRead = model.context.get("publicRead", "") != False
525
528
  auth = (65539 if publicRead else 65537) if isPublic else 0
526
- revision = ModelRevision.create(model.revision, model.revision.hash)
529
+ revision = await ModelRevision.create(model.revision, model.revision.hash)
527
530
 
528
- xVersion = int(float(os.environ.get('X_CLOUDPSS_VERSION', 4)))
529
- tags= {
530
- "replace":model.tags
531
- }
532
- if xVersion==3:
533
- tags=model.tags
534
-
535
- return graphql_request(
536
- modelQuery, {
537
- 'a': {
538
- 'rid': model.rid,
539
- 'revision': revision['hash'],
540
- 'context': model.context,
541
- 'configs': model.configs,
542
- 'jobs': model.jobs,
543
- 'name': model.name,
544
- 'description': model.description,
545
- 'tags': tags,
531
+ xVersion = int(float(os.environ.get("X_CLOUDPSS_VERSION", 4)))
532
+ tags = {"replace": model.tags}
533
+ if xVersion == 3:
534
+ tags = model.tags
535
+
536
+ return await graphql_fetch(
537
+ modelQuery,
538
+ {
539
+ "a": {
540
+ "rid": model.rid,
541
+ "revision": revision["hash"],
542
+ "context": model.context,
543
+ "configs": model.configs,
544
+ "jobs": model.jobs,
545
+ "name": model.name,
546
+ "description": model.description,
547
+ "tags": tags,
546
548
  "permissions": {
547
549
  "moderator": 1,
548
550
  "member": 1,
549
551
  "everyone": auth,
550
552
  },
551
553
  }
552
- })
554
+ },
555
+ )
553
556
 
554
557
  def fetchTopology(
555
558
  self,
@@ -558,201 +561,199 @@ class Model(object):
558
561
  maximumDepth=None,
559
562
  ):
560
563
  """
561
- 通过项目信息,获取当前项目对应的拓扑数据
564
+ 通过项目信息,获取当前项目对应的拓扑数据
562
565
 
563
- :params implementType: 实现类型
564
- :params config: config 项目参数, 不指定将使用算例保存时选中的参数方案
565
- :params maximumDepth: 最大递归深度,用于自定义项目中使用 diagram 实现元件展开情况
566
+ :params implementType: 实现类型
567
+ :params config: config 项目参数, 不指定将使用算例保存时选中的参数方案
568
+ :params maximumDepth: 最大递归深度,用于自定义项目中使用 diagram 实现元件展开情况
566
569
 
567
- :return: 一个拓扑实例
570
+ :return: 一个拓扑实例
568
571
 
569
- >>> topology=model.fetchTopology()
570
- topology=model.fetchTopology(implementType='powerFlow',config=config) # 获取潮流实现的拓扑数据
571
- topology=model.fetchTopology(maximumDepth=2) # 获取仅展开 2 层的拓扑数据
572
+ >>> topology=model.fetchTopology()
573
+ topology=model.fetchTopology(implementType='powerFlow',config=config) # 获取潮流实现的拓扑数据
574
+ topology=model.fetchTopology(maximumDepth=2) # 获取仅展开 2 层的拓扑数据
572
575
  """
573
576
 
574
577
  if self.revision is not None:
575
578
  if implementType is None:
576
- implementType = 'emtp'
579
+ implementType = "emtp"
577
580
  if config is None:
578
- currentConfig = self.context['currentConfig']
581
+ currentConfig = self.context["currentConfig"]
579
582
  config = self.configs[currentConfig]
580
- return self.revision.fetchTopology(implementType, config,
581
- maximumDepth)
583
+ return self.revision.fetchTopology(implementType, config, maximumDepth)
582
584
  return None
583
585
 
584
- def runEMT(self,job=None,config=None)->Runner[EMTResult]:
585
- '''
586
- 运行 emtp 内核,如果当前 model 没有创建 Job 时报错,默认使用第一个计算方案,进行仿真。
586
+ def runEMT(self, job=None, config=None) -> Job:
587
+ """
588
+ 运行 emtp 内核,如果当前 model 没有创建 Job 时报错,默认使用第一个计算方案,进行仿真。
589
+
590
+ :param: job 计算方案名称,可选,字符串类型或者字典类型,默认使用第一个计算方案,如果同名使用最靠前一个
591
+ :param: config 参数方案,可选,字符串类型或者字典类型,默认使用保存时选中的参数方案
587
592
 
588
- :param: job 计算方案名称,可选,字符串类型或者字典类型,默认使用第一个计算方案,如果同名使用最靠前一个
589
- :param: config 参数方案,可选,字符串类型或者字典类型,默认使用保存时选中的参数方案
590
-
591
- :return: runner Runner[EMTResult]
592
- '''
593
+ :return: job 运行结果
594
+ """
593
595
  if job is None:
594
- currentJob = self.context['currentJob']
596
+ currentJob = self.context["currentJob"]
595
597
  job = self.jobs[currentJob]
596
- if job['rid'] != 'job-definition/cloudpss/emtp':
598
+ if job["rid"] != "job-definition/cloudpss/emtp":
597
599
  for j in self.jobs:
598
- if j['rid'] == 'job-definition/cloudpss/emtp':
600
+ if j["rid"] == "job-definition/cloudpss/emtp":
599
601
  job = j
600
602
  if job is None:
601
603
  raise Exception("找不到电磁暂态运行的计算方案")
602
- if job['rid'] != 'job-definition/cloudpss/emtp':
604
+ if job["rid"] != "job-definition/cloudpss/emtp":
603
605
  raise Exception("不是电磁暂态运行生成算法的计算方案")
604
606
  if config is None:
605
- currentConfig = self.context['currentConfig']
607
+ currentConfig = self.context["currentConfig"]
606
608
  config = self.configs[currentConfig]
607
609
  return self.run(job=job, config=config)
608
610
 
609
- def runSFEMT(self,job=None,config=None)->Runner[EMTResult]:
610
- '''
611
- 运行 移频电磁暂态 内核,如果当前 model 没有创建 Job 时报错,默认使用第一个计算方案,进行仿真。
611
+ def runSFEMT(self, job=None, config=None) -> Job:
612
+ """
613
+ 运行 移频电磁暂态 内核,如果当前 model 没有创建 Job 时报错,默认使用第一个计算方案,进行仿真。
612
614
 
613
- :param: job 计算方案名称,可选,字符串类型或者字典类型,默认使用第一个计算方案,如果同名使用最靠前一个
614
- :param: config 参数方案,可选,字符串类型或者字典类型,默认使用保存时选中的参数方案
615
+ :param: job 计算方案名称,可选,字符串类型或者字典类型,默认使用第一个计算方案,如果同名使用最靠前一个
616
+ :param: config 参数方案,可选,字符串类型或者字典类型,默认使用保存时选中的参数方案
615
617
 
616
- :return: runner Runner[EMTResult]
617
- '''
618
+ :return: runner Runner[EMTResult]
619
+ """
618
620
  if job is None:
619
- currentJob = self.context['currentJob']
621
+ currentJob = self.context["currentJob"]
620
622
  job = self.jobs[currentJob]
621
- if job['rid'] != 'job-definition/cloudpss/sfemt':
623
+ if job["rid"] != "job-definition/cloudpss/sfemt":
622
624
  for j in self.jobs:
623
- if j['rid'] == 'job-definition/cloudpss/sfemt':
625
+ if j["rid"] == "job-definition/cloudpss/sfemt":
624
626
  job = j
625
627
  if job is None:
626
628
  raise Exception("找不到移频电磁暂态运行的计算方案")
627
- if job['rid'] != 'job-definition/cloudpss/sfemt':
629
+ if job["rid"] != "job-definition/cloudpss/sfemt":
628
630
  raise Exception("不是移频电磁暂态运行生成算法的计算方案")
629
631
  if config is None:
630
- currentConfig = self.context['currentConfig']
632
+ currentConfig = self.context["currentConfig"]
631
633
  config = self.configs[currentConfig]
632
634
  return self.run(job=job, config=config)
633
635
 
634
- def runPowerFlow(self,job=None,config=None)->Runner[PowerFlowResult]:
635
- '''
636
- 运行 潮流 内核,如果当前 model 没有创建 Job 时报错,默认使用第一个计算方案,进行仿真。
636
+ def runPowerFlow(self, job=None, config=None) -> Job:
637
+ """
638
+ 运行 潮流 内核,如果当前 model 没有创建 Job 时报错,默认使用第一个计算方案,进行仿真。
637
639
 
638
- :param: job 计算方案名称,可选,字符串类型或者字典类型,默认使用第一个计算方案,如果同名使用最靠前一个
639
- :param: config 参数方案,可选,字符串类型或者字典类型,默认使用保存时选中的参数方案
640
+ :param: job 计算方案名称,可选,字符串类型或者字典类型,默认使用第一个计算方案,如果同名使用最靠前一个
641
+ :param: config 参数方案,可选,字符串类型或者字典类型,默认使用保存时选中的参数方案
640
642
 
641
- :return: runner Runner[PowerFlowResult]
642
- '''
643
+ :return: runner Runner[PowerFlowResult]
644
+ """
643
645
  if job is None:
644
- currentJob = self.context['currentJob']
646
+ currentJob = self.context["currentJob"]
645
647
  job = self.jobs[currentJob]
646
- if job['rid'] != 'job-definition/cloudpss/power-flow':
648
+ if job["rid"] != "job-definition/cloudpss/power-flow":
647
649
  for j in self.jobs:
648
- if j['rid'] == 'job-definition/cloudpss/power-flow':
650
+ if j["rid"] == "job-definition/cloudpss/power-flow":
649
651
  job = j
650
652
  if job is None:
651
653
  raise Exception("找不到潮流内核运行的计算方案")
652
- if job['rid'] != 'job-definition/cloudpss/power-flow':
654
+ if job["rid"] != "job-definition/cloudpss/power-flow":
653
655
  raise Exception("不是潮流内核运行生成算法的计算方案")
654
656
  if config is None:
655
- currentConfig = self.context['currentConfig']
657
+ currentConfig = self.context["currentConfig"]
656
658
  config = self.configs[currentConfig]
657
659
  return self.run(job=job, config=config)
658
660
 
659
- def runThreePhasePowerFlow(self,job=None,config=None)->Runner[PowerFlowResult]:
660
- '''
661
- 运行 三相不平衡潮流 内核,如果当前 model 没有创建 Job 时报错,默认使用第一个计算方案,进行仿真。
662
-
663
- :param: job 计算方案名称,可选,字符串类型或者字典类型,默认使用第一个计算方案,如果同名使用最靠前一个
664
- :param: config 参数方案,可选,字符串类型或者字典类型,默认使用保存时选中的参数方案
661
+ def runThreePhasePowerFlow(self, job=None, config=None) -> Job:
662
+ """
663
+ 运行 三相不平衡潮流 内核,如果当前 model 没有创建 Job 时报错,默认使用第一个计算方案,进行仿真。
664
+
665
+ :param: job 计算方案名称,可选,字符串类型或者字典类型,默认使用第一个计算方案,如果同名使用最靠前一个
666
+ :param: config 参数方案,可选,字符串类型或者字典类型,默认使用保存时选中的参数方案
665
667
 
666
- :return: runner Runner[PowerFlowResult]
667
- '''
668
+ :return: runner Runner[PowerFlowResult]
669
+ """
668
670
  if job is None:
669
- currentJob = self.context['currentJob']
671
+ currentJob = self.context["currentJob"]
670
672
  job = self.jobs[currentJob]
671
- if job['rid'] != 'job-definition/cloudpss/three-phase-powerFlow':
673
+ if job["rid"] != "job-definition/cloudpss/three-phase-powerFlow":
672
674
  for j in self.jobs:
673
- if j['rid'] == 'job-definition/cloudpss/three-phase-powerFlow':
675
+ if j["rid"] == "job-definition/cloudpss/three-phase-powerFlow":
674
676
  job = j
675
677
  if job is None:
676
678
  raise Exception("找不到三相不平衡潮流内核运行的计算方案")
677
- if job['rid'] != 'job-definition/cloudpss/three-phase-powerFlow':
679
+ if job["rid"] != "job-definition/cloudpss/three-phase-powerFlow":
678
680
  raise Exception("不是三相不平衡潮流内核运行生成算法的计算方案")
679
681
  if config is None:
680
- currentConfig = self.context['currentConfig']
682
+ currentConfig = self.context["currentConfig"]
681
683
  config = self.configs[currentConfig]
682
684
  return self.run(job=job, config=config)
683
-
684
- def runIESLoadPrediction(self,job=None,config=None)->Runner[IESResult]:
685
- '''
686
- 运行 负荷预测方案 内核,如果当前 model 没有创建 Job 时报错,默认使用第一个计算方案,进行仿真。
687
-
688
- :param: job 计算方案名称,可选,字符串类型或者字典类型,默认使用第一个计算方案,如果同名使用最靠前一个
689
- :param: config 参数方案,可选,字符串类型或者字典类型,默认使用保存时选中的参数方案
690
-
691
- :return: runner Runner[IESResult]
692
- '''
685
+
686
+ def runIESLoadPrediction(self, job=None, config=None) -> Runner[IESResult]:
687
+ """
688
+ 运行 负荷预测方案 内核,如果当前 model 没有创建 Job 时报错,默认使用第一个计算方案,进行仿真。
689
+
690
+ :param: job 计算方案名称,可选,字符串类型或者字典类型,默认使用第一个计算方案,如果同名使用最靠前一个
691
+ :param: config 参数方案,可选,字符串类型或者字典类型,默认使用保存时选中的参数方案
692
+
693
+ :return: runner Runner[IESResult]
694
+ """
693
695
  if job is None:
694
- currentJob = self.context['currentJob']
696
+ currentJob = self.context["currentJob"]
695
697
  job = self.jobs[currentJob]
696
- if job['rid'] != 'job-definition/ies/ies-load-prediction':
698
+ if job["rid"] != "job-definition/ies/ies-load-prediction":
697
699
  for j in self.jobs:
698
- if j['rid'] == 'job-definition/ies/ies-load-prediction':
700
+ if j["rid"] == "job-definition/ies/ies-load-prediction":
699
701
  job = j
700
702
  if job is None:
701
703
  raise Exception("找不到负荷预测方案内核运行的计算方案")
702
- if job['rid'] != 'job-definition/ies/ies-load-prediction':
704
+ if job["rid"] != "job-definition/ies/ies-load-prediction":
703
705
  raise Exception("不是负荷预测方案内核运行生成算法的计算方案")
704
706
  if config is None:
705
- currentConfig = self.context['currentConfig']
707
+ currentConfig = self.context["currentConfig"]
706
708
  config = self.configs[currentConfig]
707
709
  return self.run(job=job, config=config)
708
-
709
- def runIESPowerFlow(self,job=None,config=None)->Runner[IESResult]:
710
- '''
711
- 运行 时序潮流方案 内核,如果当前 model 没有创建 Job 时报错,默认使用第一个计算方案,进行仿真。
712
-
713
- :param: job 计算方案名称,可选,字符串类型或者字典类型,默认使用第一个计算方案,如果同名使用最靠前一个
714
- :param: config 参数方案,可选,字符串类型或者字典类型,默认使用保存时选中的参数方案
715
-
716
- :return: runner Runner[IESResult]
717
- '''
710
+
711
+ def runIESPowerFlow(self, job=None, config=None) -> Runner[IESResult]:
712
+ """
713
+ 运行 时序潮流方案 内核,如果当前 model 没有创建 Job 时报错,默认使用第一个计算方案,进行仿真。
714
+
715
+ :param: job 计算方案名称,可选,字符串类型或者字典类型,默认使用第一个计算方案,如果同名使用最靠前一个
716
+ :param: config 参数方案,可选,字符串类型或者字典类型,默认使用保存时选中的参数方案
717
+
718
+ :return: runner Runner[IESResult]
719
+ """
718
720
  if job is None:
719
- currentJob = self.context['currentJob']
721
+ currentJob = self.context["currentJob"]
720
722
  job = self.jobs[currentJob]
721
- if job['rid'] != 'job-definition/ies/ies-power-flow':
723
+ if job["rid"] != "job-definition/ies/ies-power-flow":
722
724
  for j in self.jobs:
723
- if j['rid'] == 'job-definition/ies/ies-power-flow':
725
+ if j["rid"] == "job-definition/ies/ies-power-flow":
724
726
  job = j
725
727
  if job is None:
726
728
  raise Exception("找不到时序潮流方案内核运行的计算方案")
727
- if job['rid'] != 'job-definition/ies/ies-power-flow':
729
+ if job["rid"] != "job-definition/ies/ies-power-flow":
728
730
  raise Exception("不是时序潮流方案内核运行生成算法的计算方案")
729
731
  if config is None:
730
- currentConfig = self.context['currentConfig']
732
+ currentConfig = self.context["currentConfig"]
731
733
  config = self.configs[currentConfig]
732
734
  return self.run(job=job, config=config)
733
735
 
734
- def runIESEnergyStoragePlan(self,job=None,config=None)->Runner[IESResult]:
735
- '''
736
- 运行 储能规划方案 内核,如果当前 model 没有创建 Job 时报错,默认使用第一个计算方案,进行仿真。
737
-
738
- :param: job 计算方案名称,可选,字符串类型或者字典类型,默认使用第一个计算方案,如果同名使用最靠前一个
739
- :param: config 参数方案,可选,字符串类型或者字典类型,默认使用保存时选中的参数方案
736
+ def runIESEnergyStoragePlan(self, job=None, config=None) -> Runner[IESResult]:
737
+ """
738
+ 运行 储能规划方案 内核,如果当前 model 没有创建 Job 时报错,默认使用第一个计算方案,进行仿真。
739
+
740
+ :param: job 计算方案名称,可选,字符串类型或者字典类型,默认使用第一个计算方案,如果同名使用最靠前一个
741
+ :param: config 参数方案,可选,字符串类型或者字典类型,默认使用保存时选中的参数方案
740
742
 
741
- :return: runner Runner[IESResult]
742
- '''
743
+ :return: runner Runner[IESResult]
744
+ """
743
745
  if job is None:
744
- currentJob = self.context['currentJob']
746
+ currentJob = self.context["currentJob"]
745
747
  job = self.jobs[currentJob]
746
- if job['rid'] != 'job-definition/ies/ies-energy-storage-plan':
748
+ if job["rid"] != "job-definition/ies/ies-energy-storage-plan":
747
749
  for j in self.jobs:
748
- if j['rid'] == 'job-definition/ies/ies-energy-storage-plan':
750
+ if j["rid"] == "job-definition/ies/ies-energy-storage-plan":
749
751
  job = j
750
752
  if job is None:
751
753
  raise Exception("找不到储能规划方案内核运行的计算方案")
752
- if job['rid'] != 'job-definition/ies/ies-energy-storage-plan':
754
+ if job["rid"] != "job-definition/ies/ies-energy-storage-plan":
753
755
  raise Exception("不是储能规划方案内核运行生成算法的计算方案")
754
756
  if config is None:
755
- currentConfig = self.context['currentConfig']
757
+ currentConfig = self.context["currentConfig"]
756
758
  config = self.configs[currentConfig]
757
759
  return self.run(job=job, config=config)
758
-