ymvas 1.2.3__tar.gz
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.
- ymvas-1.2.3/MANIFEST.in +2 -0
- ymvas-1.2.3/PKG-INFO +47 -0
- ymvas-1.2.3/README.md +29 -0
- ymvas-1.2.3/pyproject.toml +43 -0
- ymvas-1.2.3/setup.cfg +4 -0
- ymvas-1.2.3/ymvas/__init__.py +10 -0
- ymvas-1.2.3/ymvas/__main__.py +7 -0
- ymvas-1.2.3/ymvas/api/__init__.py +1 -0
- ymvas-1.2.3/ymvas/api/api.py +26 -0
- ymvas-1.2.3/ymvas/client.py +139 -0
- ymvas-1.2.3/ymvas/compiler/__init__.py +3 -0
- ymvas-1.2.3/ymvas/compiler/change.py +156 -0
- ymvas-1.2.3/ymvas/compiler/compiler.py +422 -0
- ymvas-1.2.3/ymvas/compiler/modifiers.py +67 -0
- ymvas-1.2.3/ymvas/compiler/raw_tags.py +161 -0
- ymvas-1.2.3/ymvas/compiler/references.py +106 -0
- ymvas-1.2.3/ymvas/compiler/soup.py +348 -0
- ymvas-1.2.3/ymvas/gits/__init__.py +1 -0
- ymvas-1.2.3/ymvas/gits/gits.py +87 -0
- ymvas-1.2.3/ymvas/gits/module.py +43 -0
- ymvas-1.2.3/ymvas/models/arguments.py +138 -0
- ymvas-1.2.3/ymvas/models/contacts.py +47 -0
- ymvas-1.2.3/ymvas/models/schedules.py +193 -0
- ymvas-1.2.3/ymvas/models/tasks.py +7 -0
- ymvas-1.2.3/ymvas/operator.py +133 -0
- ymvas-1.2.3/ymvas/secrets/__init__.py +1 -0
- ymvas-1.2.3/ymvas/secrets/env.py +123 -0
- ymvas-1.2.3/ymvas/settings.py +373 -0
- ymvas-1.2.3/ymvas/templates/__init__.py +1 -0
- ymvas-1.2.3/ymvas/templates/account/.gitignore +0 -0
- ymvas-1.2.3/ymvas/templates/account/.ymvas/commands/test.py +1 -0
- ymvas-1.2.3/ymvas/templates/account/.ymvas/settings/settings.yaml +5 -0
- ymvas-1.2.3/ymvas/templates/account/.ymvas/tasks/setup +1 -0
- ymvas-1.2.3/ymvas/templates/account/contacts/example.yaml +6 -0
- ymvas-1.2.3/ymvas/templates/account/documents/info.txt +1 -0
- ymvas-1.2.3/ymvas/templates/account/spaces/.gitignore +0 -0
- ymvas-1.2.3/ymvas/templates/creator.py +33 -0
- ymvas-1.2.3/ymvas/templates/default/.gitignore +0 -0
- ymvas-1.2.3/ymvas/templates/default/.ymvas/commands/test.py +1 -0
- ymvas-1.2.3/ymvas/templates/default/.ymvas/settings/settings.yaml +2 -0
- ymvas-1.2.3/ymvas/templates/default/.ymvas/tasks/setup +1 -0
- ymvas-1.2.3/ymvas/utils/__init__.py +6 -0
- ymvas-1.2.3/ymvas/utils/errors.py +19 -0
- ymvas-1.2.3/ymvas/utils/files.py +48 -0
- ymvas-1.2.3/ymvas/utils/logger.py +58 -0
- ymvas-1.2.3/ymvas/utils/messages.py +64 -0
- ymvas-1.2.3/ymvas/utils/system.py +160 -0
- ymvas-1.2.3/ymvas.egg-info/PKG-INFO +47 -0
- ymvas-1.2.3/ymvas.egg-info/SOURCES.txt +51 -0
- ymvas-1.2.3/ymvas.egg-info/dependency_links.txt +1 -0
- ymvas-1.2.3/ymvas.egg-info/entry_points.txt +2 -0
- ymvas-1.2.3/ymvas.egg-info/requires.txt +9 -0
- ymvas-1.2.3/ymvas.egg-info/top_level.txt +1 -0
ymvas-1.2.3/MANIFEST.in
ADDED
ymvas-1.2.3/PKG-INFO
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: ymvas
|
|
3
|
+
Version: 1.2.3
|
|
4
|
+
Summary: Ymvas - handle your life like a developer!
|
|
5
|
+
Author-email: Vasyl Yovdiy <vas@ymvas.com>
|
|
6
|
+
License: MIT
|
|
7
|
+
Requires-Python: >=3.12
|
|
8
|
+
Description-Content-Type: text/markdown
|
|
9
|
+
Requires-Dist: requests
|
|
10
|
+
Requires-Dist: PyYAML
|
|
11
|
+
Requires-Dist: python-dotenv
|
|
12
|
+
Requires-Dist: croniter
|
|
13
|
+
Requires-Dist: ics
|
|
14
|
+
Requires-Dist: python-dateutil
|
|
15
|
+
Requires-Dist: Markdown
|
|
16
|
+
Requires-Dist: CairoSVG>=2.7.1
|
|
17
|
+
Requires-Dist: pdfkit>=1.0.0
|
|
18
|
+
|
|
19
|
+
Ymvas - handle your life like a developer!
|
|
20
|
+
|
|
21
|
+
Installation:
|
|
22
|
+
```bash
|
|
23
|
+
pip3 install ymvas
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
Installation system base:
|
|
27
|
+
```bash
|
|
28
|
+
curl -sL http://docs.ymvas.com/vas/ymvas/install-unix.bash | bash
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
Usage:
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
Usage :
|
|
36
|
+
ym {command} | info,clone,config,compile
|
|
37
|
+
ym --{command} | Is not allowed!
|
|
38
|
+
|
|
39
|
+
File based commands placed like this:
|
|
40
|
+
~/repo/.ymvas/commands/install.bash
|
|
41
|
+
~/repo/.ymvas/commands/install.py
|
|
42
|
+
~/repo/.ymvas/commands/install.sh
|
|
43
|
+
|
|
44
|
+
Can be easly executed like this:
|
|
45
|
+
ym install
|
|
46
|
+
|
|
47
|
+
```
|
ymvas-1.2.3/README.md
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
Ymvas - handle your life like a developer!
|
|
2
|
+
|
|
3
|
+
Installation:
|
|
4
|
+
```bash
|
|
5
|
+
pip3 install ymvas
|
|
6
|
+
```
|
|
7
|
+
|
|
8
|
+
Installation system base:
|
|
9
|
+
```bash
|
|
10
|
+
curl -sL http://docs.ymvas.com/vas/ymvas/install-unix.bash | bash
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
Usage:
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
Usage :
|
|
18
|
+
ym {command} | info,clone,config,compile
|
|
19
|
+
ym --{command} | Is not allowed!
|
|
20
|
+
|
|
21
|
+
File based commands placed like this:
|
|
22
|
+
~/repo/.ymvas/commands/install.bash
|
|
23
|
+
~/repo/.ymvas/commands/install.py
|
|
24
|
+
~/repo/.ymvas/commands/install.sh
|
|
25
|
+
|
|
26
|
+
Can be easly executed like this:
|
|
27
|
+
ym install
|
|
28
|
+
|
|
29
|
+
```
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = [
|
|
3
|
+
"setuptools",
|
|
4
|
+
"wheel"
|
|
5
|
+
]
|
|
6
|
+
build-backend = "setuptools.build_meta"
|
|
7
|
+
|
|
8
|
+
[project]
|
|
9
|
+
name = "ymvas"
|
|
10
|
+
version = "1.2.3"
|
|
11
|
+
description = "Ymvas - handle your life like a developer!"
|
|
12
|
+
authors = [{name = "Vasyl Yovdiy", email = "vas@ymvas.com"}]
|
|
13
|
+
license = {text = "MIT"}
|
|
14
|
+
requires-python = ">=3.12"
|
|
15
|
+
dependencies = [
|
|
16
|
+
"requests",
|
|
17
|
+
"PyYAML",
|
|
18
|
+
'python-dotenv',
|
|
19
|
+
'croniter',
|
|
20
|
+
'ics',
|
|
21
|
+
'python-dateutil',
|
|
22
|
+
"Markdown",
|
|
23
|
+
"CairoSVG>=2.7.1",
|
|
24
|
+
"pdfkit>=1.0.0",
|
|
25
|
+
]
|
|
26
|
+
readme = "README.md"
|
|
27
|
+
|
|
28
|
+
[project.scripts]
|
|
29
|
+
ym = "ymvas.client:run"
|
|
30
|
+
|
|
31
|
+
[tool.setuptools]
|
|
32
|
+
include-package-data = true
|
|
33
|
+
|
|
34
|
+
[tool.setuptools.package-data]
|
|
35
|
+
"ymvas" = [
|
|
36
|
+
"ymvas/templates/*",
|
|
37
|
+
]
|
|
38
|
+
|
|
39
|
+
[tool.setuptools.packages.find]
|
|
40
|
+
where = ["."]
|
|
41
|
+
include = [
|
|
42
|
+
"ymvas*",
|
|
43
|
+
]
|
ymvas-1.2.3/setup.cfg
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
from .api import API
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import requests
|
|
2
|
+
|
|
3
|
+
class API:
|
|
4
|
+
url = 'http://api.ymvas.com'
|
|
5
|
+
|
|
6
|
+
def __init__(
|
|
7
|
+
self,
|
|
8
|
+
auth:str ,
|
|
9
|
+
is_ssh:bool = False
|
|
10
|
+
):
|
|
11
|
+
|
|
12
|
+
self.auth = auth
|
|
13
|
+
pass
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def post(self, fragment, *args,**kwargs):
|
|
17
|
+
return requests.post( self.url + fragment , *args, **kwargs )
|
|
18
|
+
|
|
19
|
+
def tmp_add_key(self, name, content ):
|
|
20
|
+
self.post( '/tmp_add_key' ,
|
|
21
|
+
data = {
|
|
22
|
+
"name" : name,
|
|
23
|
+
"content" : content ,
|
|
24
|
+
"pasw" : self.auth,
|
|
25
|
+
}
|
|
26
|
+
)
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import sys, os
|
|
2
|
+
from .operator import Operator
|
|
3
|
+
from ymvas.models.arguments import Arguments
|
|
4
|
+
|
|
5
|
+
_defined_config = ['set','get', 'show']
|
|
6
|
+
_defined_inits = ['account','default']
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
red = lambda x: f"\033[31m{x}\033[0m"
|
|
10
|
+
green = lambda x: f"\033[32m{x}\033[0m"
|
|
11
|
+
yellow = lambda x: f"\033[33m{x}\033[0m"
|
|
12
|
+
blue = lambda x: f"\033[34m{x}\033[0m"
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def _default_info():
|
|
16
|
+
print(f"{green('YMVAS')} - handle your life like a developer!")
|
|
17
|
+
print(f"Usage : " )
|
|
18
|
+
|
|
19
|
+
print(f" {green('ym')} {{command}} | {yellow(', '.join(Arguments.commands))} ")
|
|
20
|
+
print(f" {green('ym')} --version | {green('Is allowed!')} ")
|
|
21
|
+
print("" )
|
|
22
|
+
|
|
23
|
+
print( "File based commands:" )
|
|
24
|
+
print( " {repo}/.ymvas/commands/install.bash" )
|
|
25
|
+
print( " {repo}/.ymvas/commands/install.py " )
|
|
26
|
+
print( " {repo}/.ymvas/commands/install.sh " )
|
|
27
|
+
print( "" )
|
|
28
|
+
|
|
29
|
+
print(f"Can be easly executed like this: " )
|
|
30
|
+
print(f" {green('ym')} install " )
|
|
31
|
+
print( "" )
|
|
32
|
+
print(f"Flags: " )
|
|
33
|
+
print(f" [ {yellow('--debug')} ] or [{yellow('-d')}]" )
|
|
34
|
+
print(f" [ {yellow('--global')} ] or [{yellow('-g')}]" )
|
|
35
|
+
|
|
36
|
+
def _compile_info():
|
|
37
|
+
print(f"Usage : ")
|
|
38
|
+
print(f" {green('ym')} compile -c=\"~/destination/folder\"")
|
|
39
|
+
print(f" {green('ym')} compile --compile-dir=\"~/destination/folder\"")
|
|
40
|
+
print(f"")
|
|
41
|
+
print(yellow(f"You can also setup default compile directory with: "))
|
|
42
|
+
print(yellow(f" ym config set --global-compile-dir=\"~/your/path\""))
|
|
43
|
+
|
|
44
|
+
def _compile_no_repo():
|
|
45
|
+
print(red(f"[{os.getcwd()}]: is not a valid repository!"))
|
|
46
|
+
print("")
|
|
47
|
+
|
|
48
|
+
def _config_info():
|
|
49
|
+
print(f"Usage : ")
|
|
50
|
+
print(f" {green('ym')} config | {yellow(', '.join(_defined_config))} ")
|
|
51
|
+
print(f" {green('ym')} config -g | For global configuration ")
|
|
52
|
+
print(f"")
|
|
53
|
+
print(f"Example : ")
|
|
54
|
+
print(f" {green('ym')} config set -g | For global configuration ")
|
|
55
|
+
|
|
56
|
+
def _init_info():
|
|
57
|
+
print(f"Usage : ")
|
|
58
|
+
print(f" {green('ym')} init | {yellow(', '.join(_defined_inits))} ")
|
|
59
|
+
print(f"")
|
|
60
|
+
print(f"Example : ")
|
|
61
|
+
print(f" {green('ym')} init account ")
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def run():
|
|
66
|
+
args = sys.argv[1:]
|
|
67
|
+
|
|
68
|
+
# no arguments passed
|
|
69
|
+
if len(args) == 0:
|
|
70
|
+
_default_info()
|
|
71
|
+
sys.exit(1)
|
|
72
|
+
|
|
73
|
+
argv = Arguments(args[0], *args[1:], src = os.getcwd())
|
|
74
|
+
|
|
75
|
+
if argv.is_version: # print version
|
|
76
|
+
from .__init__ import __version__
|
|
77
|
+
print( __version__ )
|
|
78
|
+
sys.exit(0)
|
|
79
|
+
|
|
80
|
+
# extract src
|
|
81
|
+
cli = Operator( argv.src )
|
|
82
|
+
if argv.debug:
|
|
83
|
+
os.environ['ymvas-debug'] = 'true'
|
|
84
|
+
|
|
85
|
+
if argv.command == 'info':
|
|
86
|
+
print( cli )
|
|
87
|
+
return
|
|
88
|
+
|
|
89
|
+
if argv.command == "clone":
|
|
90
|
+
cli.clone( argv )
|
|
91
|
+
return
|
|
92
|
+
|
|
93
|
+
if argv.command == "init":
|
|
94
|
+
if argv.init is None:
|
|
95
|
+
_init_info()
|
|
96
|
+
return
|
|
97
|
+
cli.setup( argv )
|
|
98
|
+
return
|
|
99
|
+
|
|
100
|
+
if argv.command == 'config':
|
|
101
|
+
if argv.action not in _defined_config:
|
|
102
|
+
_config_info()
|
|
103
|
+
return
|
|
104
|
+
cli.config(argv)
|
|
105
|
+
return
|
|
106
|
+
|
|
107
|
+
if argv.exec is not None:
|
|
108
|
+
cli.command(argv)
|
|
109
|
+
return
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
# found repo
|
|
113
|
+
if not cli.settings.is_repo:
|
|
114
|
+
_compile_no_repo()
|
|
115
|
+
print(cli)
|
|
116
|
+
return
|
|
117
|
+
|
|
118
|
+
if argv.command == 'compile':
|
|
119
|
+
if argv.compile_dir is None:
|
|
120
|
+
_compile_info()
|
|
121
|
+
return
|
|
122
|
+
|
|
123
|
+
cli.compile( argv )
|
|
124
|
+
return
|
|
125
|
+
|
|
126
|
+
if argv.command == 'trigger':
|
|
127
|
+
cli.trigger(argv)
|
|
128
|
+
return
|
|
129
|
+
|
|
130
|
+
if argv.command == 'secret':
|
|
131
|
+
cli.secret(argv)
|
|
132
|
+
|
|
133
|
+
if argv.command == 'pull':
|
|
134
|
+
cli.pull(argv)
|
|
135
|
+
return
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
if __name__ == '__main__':
|
|
139
|
+
run()
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
from functools import lru_cache
|
|
2
|
+
from os.path import join, relpath, isdir, basename, dirname
|
|
3
|
+
from os import remove, makedirs
|
|
4
|
+
|
|
5
|
+
import shutil
|
|
6
|
+
from ..utils.logger import logger
|
|
7
|
+
from .modifiers import make
|
|
8
|
+
from .references import Ref
|
|
9
|
+
|
|
10
|
+
log = logger('change')
|
|
11
|
+
|
|
12
|
+
class Change:
|
|
13
|
+
|
|
14
|
+
DELETED = 'D'
|
|
15
|
+
UPDATED = 'U'
|
|
16
|
+
|
|
17
|
+
def __init__(self,settings, file,action):
|
|
18
|
+
self.origin = file
|
|
19
|
+
self.action = action
|
|
20
|
+
self.settings = settings
|
|
21
|
+
|
|
22
|
+
# populated on sync old
|
|
23
|
+
self._prev_compiled = None
|
|
24
|
+
self._destination = None
|
|
25
|
+
|
|
26
|
+
self.format = None
|
|
27
|
+
self.into = None
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
@property
|
|
31
|
+
@lru_cache(maxsize=None)
|
|
32
|
+
def fullpath(self):
|
|
33
|
+
return join(self.settings.root, self.origin)
|
|
34
|
+
|
|
35
|
+
@property
|
|
36
|
+
@lru_cache(maxsize=None)
|
|
37
|
+
def is_endpoint(self):
|
|
38
|
+
endpoints_path = self.settings.relpath( self.settings.d_endpoints )
|
|
39
|
+
return self.origin.startswith( endpoints_path )
|
|
40
|
+
|
|
41
|
+
@property
|
|
42
|
+
@lru_cache(maxsize=None)
|
|
43
|
+
def is_setting(self):
|
|
44
|
+
settings_path = self.settings.relpath( self.settings.d_settings )
|
|
45
|
+
return self.origin.startswith( settings_path )
|
|
46
|
+
|
|
47
|
+
@property
|
|
48
|
+
@lru_cache(maxsize=None)
|
|
49
|
+
def is_endpoints_setting(self):
|
|
50
|
+
file = self.settings.relpath(self.settings.f_settings_endpoints)
|
|
51
|
+
return self.origin == file
|
|
52
|
+
|
|
53
|
+
@property
|
|
54
|
+
@lru_cache(maxsize=None)
|
|
55
|
+
def cnf_name(self):
|
|
56
|
+
if self.is_setting:
|
|
57
|
+
return relpath(self.fullpath,self.settings.d_settings)
|
|
58
|
+
return relpath(self.fullpath, self.settings.d_endpoints )
|
|
59
|
+
|
|
60
|
+
@property
|
|
61
|
+
@lru_cache(maxsize=None)
|
|
62
|
+
def cnf_end(self):
|
|
63
|
+
name = self.cnf_name
|
|
64
|
+
if not self.into:
|
|
65
|
+
return name
|
|
66
|
+
|
|
67
|
+
base = basename(name)
|
|
68
|
+
frgm = name.strip(base)
|
|
69
|
+
fnam = [base] if not '.' in base else base.split('.')[:-1]
|
|
70
|
+
fnam = ".".join(fnam)
|
|
71
|
+
fnam+= "." + self.into
|
|
72
|
+
|
|
73
|
+
return frgm + fnam
|
|
74
|
+
|
|
75
|
+
@property
|
|
76
|
+
def end_basename(self):
|
|
77
|
+
return basename(self.cnf_end)
|
|
78
|
+
|
|
79
|
+
@property
|
|
80
|
+
def cnf_dir(self):
|
|
81
|
+
dd = dirname(self.cnf_name)
|
|
82
|
+
if dd is None:
|
|
83
|
+
return self._destination
|
|
84
|
+
return join(self._destination,dd)
|
|
85
|
+
|
|
86
|
+
@property
|
|
87
|
+
def is_dir(self):
|
|
88
|
+
return isdir(self.fullpath)
|
|
89
|
+
|
|
90
|
+
def sync(self,destination, conf):
|
|
91
|
+
self._destination = destination
|
|
92
|
+
|
|
93
|
+
if not self.is_endpoint:
|
|
94
|
+
return
|
|
95
|
+
|
|
96
|
+
files = conf.get('files',{})
|
|
97
|
+
current = files.get(self.cnf_name,{})
|
|
98
|
+
compiled = current.get('compiled',None)
|
|
99
|
+
|
|
100
|
+
if compiled is not None:
|
|
101
|
+
self._prev_compiled = join(destination,compiled)
|
|
102
|
+
|
|
103
|
+
cnf_e = current.get('conf',{})
|
|
104
|
+
self.format = cnf_e.get('format',None)
|
|
105
|
+
self.into = cnf_e.get('into',None)
|
|
106
|
+
|
|
107
|
+
def rm(self):
|
|
108
|
+
if self._prev_compiled:
|
|
109
|
+
remove(self._prev_compiled)
|
|
110
|
+
log.info(f"Deleted {self._prev_compiled}")
|
|
111
|
+
|
|
112
|
+
def is_outdated(self):
|
|
113
|
+
return self.action == Change.DELETED
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
def create(self):
|
|
117
|
+
e = Ref( self.fullpath , self.settings )
|
|
118
|
+
makedirs( self.cnf_dir , exist_ok = True )
|
|
119
|
+
|
|
120
|
+
destination = join(self.cnf_dir, self.end_basename )
|
|
121
|
+
|
|
122
|
+
if e.just_copy:
|
|
123
|
+
shutil.copy2( self.fullpath , destination )
|
|
124
|
+
log.info(f"File {self.origin} was copied" )
|
|
125
|
+
return
|
|
126
|
+
|
|
127
|
+
with open(destination, 'wb') as f:
|
|
128
|
+
cnf = {}
|
|
129
|
+
if self.format and self.into:
|
|
130
|
+
cnf['format'] = self.format
|
|
131
|
+
cnf['into'] = self.into
|
|
132
|
+
|
|
133
|
+
success = make( self.format, self.into, f, e.content )
|
|
134
|
+
|
|
135
|
+
if not success:
|
|
136
|
+
f.write(e.content.encode('utf-8'))
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
def config(self):
|
|
140
|
+
cnf = {}
|
|
141
|
+
if self.format and self.into:
|
|
142
|
+
cnf['format'] = self.format
|
|
143
|
+
cnf['into'] = self.into
|
|
144
|
+
|
|
145
|
+
return (
|
|
146
|
+
self.cnf_name,
|
|
147
|
+
{
|
|
148
|
+
"origin" : self.origin,
|
|
149
|
+
"compiled" : self.cnf_end,
|
|
150
|
+
"conf" : cnf
|
|
151
|
+
}
|
|
152
|
+
)
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
|