Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
S
SMART
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
3
Issues
3
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Registry
Registry
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
UNI-KLU
SMART
Commits
15615084
Commit
15615084
authored
Jul 06, 2021
by
Manuel
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'feature/trust' into develop
parents
3f61d953
022ae5f0
Changes
29
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
29 changed files
with
1445 additions
and
24 deletions
+1445
-24
Dockerfile
src/data-hub/reputation-calculation-microservice/Dockerfile
+6
-0
routes.yml
...eputation-calculation-microservice/app/configs/routes.yml
+413
-0
swagger.yml
...putation-calculation-microservice/app/configs/swagger.yml
+5
-16
swagger_local.yml
...on-calculation-microservice/app/configs/swagger_local.yml
+19
-0
context.py
...ulation-microservice/app/lib/database/entities/context.py
+19
-0
trust_adapter.py
...n-microservice/app/lib/database/entities/trust_adapter.py
+58
-0
trust_trace.py
...ion-microservice/app/lib/database/entities/trust_trace.py
+38
-0
user.py
...alculation-microservice/app/lib/database/entities/user.py
+30
-0
user_trust.py
...tion-microservice/app/lib/database/entities/user_trust.py
+27
-0
context_repository.py
...rvice/app/lib/database/repositories/context_repository.py
+28
-0
trust_adapter_repository.py
...app/lib/database/repositories/trust_adapter_repository.py
+57
-0
trust_trace_repository.py
...e/app/lib/database/repositories/trust_trace_repository.py
+34
-0
user_repository.py
...oservice/app/lib/database/repositories/user_repository.py
+59
-0
user_trust_repository.py
...ce/app/lib/database/repositories/user_trust_repository.py
+54
-0
context.py
...tation-calculation-microservice/app/lib/routes/context.py
+24
-0
traces.py
...utation-calculation-microservice/app/lib/routes/traces.py
+20
-0
trust_adapter.py
...-calculation-microservice/app/lib/routes/trust_adapter.py
+57
-0
user.py
...eputation-calculation-microservice/app/lib/routes/user.py
+24
-0
trust_service.py
...alculation-microservice/app/lib/services/trust_service.py
+77
-0
message_handler.py
...calculation-microservice/app/lib/trust/message_handler.py
+48
-0
node.py
...reputation-calculation-microservice/app/lib/trust/node.py
+3
-0
oracle.py
...putation-calculation-microservice/app/lib/trust/oracle.py
+52
-0
transaction.py
...ion-calculation-microservice/app/lib/trust/transaction.py
+5
-0
main.py
src/data-hub/reputation-calculation-microservice/app/main.py
+45
-4
requirements.txt
.../reputation-calculation-microservice/app/requirements.txt
+0
-0
test_messaging.py
...tion-calculation-microservice/app/tests/test_messaging.py
+170
-0
deployment.yml
...tation-calculation-microservice/deployment/deployment.yml
+55
-1
network_constants.py
src/modules/network_constants.py
+13
-1
Dockerfile
src/rest-gateway/Dockerfile
+5
-2
No files found.
src/data-hub/reputation-calculation-microservice/Dockerfile
View file @
15615084
...
...
@@ -8,7 +8,13 @@ RUN pip install connexion[swagger-ui]
EXPOSE
5000
WORKDIR
/app
COPY
src/data-hub/reputation-calculation-microservice/app/requirements.txt /app/
RUN
pip
install
-r
requirements.txt
COPY
src/data-hub/reputation-calculation-microservice/app/ /app/
COPY
src/modules/ /app/
RUN
chmod
a+x main.py
CMD
["python", "./main.py"]
\ No newline at end of file
src/data-hub/reputation-calculation-microservice/app/configs/routes.yml
0 → 100644
View file @
15615084
This diff is collapsed.
Click to expand it.
src/data-hub/reputation-calculation-microservice/app/configs/swagger.yml
View file @
15615084
...
...
@@ -11,20 +11,9 @@ produces:
basePath
:
"
/api"
# Import security definitions from seperate file
securityDefinitions
:
$ref
:
'
../security/security.yml#securityDefinitions'
paths
:
/debug
:
post
:
operationId
:
"
debug.echo"
tags
:
-
"
Echo"
summary
:
"
Echo
function
for
debugging
purposes"
description
:
"
Echoes
the
input
back
to
the
caller."
parameters
:
-
in
:
body
name
:
"
Object"
required
:
true
schema
:
type
:
object
responses
:
200
:
description
:
"
Successful
echo
of
request
data"
$ref
:
'
routes.yml#paths'
\ No newline at end of file
src/data-hub/reputation-calculation-microservice/app/configs/swagger_local.yml
0 → 100644
View file @
15615084
swagger
:
"
2.0"
info
:
title
:
Reputation Calculation microservice
description
:
This is the documentation for the reputation calculation microservice.
version
:
"
1.0.0"
consumes
:
-
"
application/json"
produces
:
-
"
application/json"
basePath
:
"
/api"
# Import security definitions from seperate file
securityDefinitions
:
$ref
:
'
../../../../modules/security/security_local.yml#securityDefinitions'
paths
:
$ref
:
'
routes.yml#paths'
\ No newline at end of file
src/data-hub/reputation-calculation-microservice/app/lib/database/entities/context.py
0 → 100644
View file @
15615084
from
typing
import
Dict
class
Context
:
def
__init__
(
self
,
use_case
:
str
,
name
:
str
):
self
.
use_case
=
use_case
self
.
name
=
name
def
to_serializable_dict
(
self
):
return
{
"use_case"
:
self
.
use_case
,
"name"
:
self
.
name
}
@
staticmethod
def
from_serializable_dict
(
data
:
Dict
):
if
"use_case"
not
in
data
.
keys
()
or
"name"
not
in
data
.
keys
():
raise
ValueError
(
"Missing fields!"
)
return
Context
(
data
[
"use_case"
],
data
[
"name"
])
\ No newline at end of file
src/data-hub/reputation-calculation-microservice/app/lib/database/entities/trust_adapter.py
0 → 100644
View file @
15615084
from
typing
import
Dict
class
TrustAdapter
:
def
__init__
(
self
,
use_case
:
str
,
context
:
str
,
table
:
str
,
user_source
:
str
,
volume_source
:
str
,
rating_source
:
str
,
rating_lower
:
float
,
rating_upper
:
float
,
conversion
:
float
,
certainty
:
float
,
cutoff
:
float
,
bias_negative
:
float
,
bias_positive
:
float
):
self
.
use_case
=
use_case
self
.
context
=
context
self
.
table
=
table
self
.
user_source
=
user_source
self
.
volume_source
=
volume_source
self
.
rating_source
=
rating_source
self
.
rating_lower
=
rating_lower
self
.
rating_upper
=
rating_upper
self
.
conversion
=
conversion
self
.
certainty
=
certainty
self
.
cutoff
=
cutoff
self
.
bias_negative
=
bias_negative
self
.
bias_positive
=
bias_positive
def
to_serializable_dict
(
self
):
return
{
"use_case"
:
self
.
use_case
,
"context"
:
self
.
context
,
"table"
:
self
.
table
,
"user_source"
:
self
.
user_source
,
"volume_source"
:
self
.
volume_source
,
"rating_source"
:
self
.
rating_source
,
"rating_lower"
:
self
.
rating_lower
,
"rating_upper"
:
self
.
rating_upper
,
"conversion"
:
self
.
conversion
,
"certainty"
:
self
.
certainty
,
"cutoff"
:
self
.
cutoff
,
"bias_negative"
:
self
.
bias_negative
,
"bias_positive"
:
self
.
bias_positive
}
@
staticmethod
def
from_serializable_dict
(
data
:
Dict
):
if
"use_case"
not
in
data
.
keys
()
or
"context"
not
in
data
.
keys
()
or
"table"
not
in
data
.
keys
()
or
"user_source"
not
in
data
.
keys
()
or
"volume_source"
not
in
data
.
keys
()
or
"rating_source"
not
in
data
.
keys
()
or
"conversion"
not
in
data
.
keys
()
or
"certainty"
not
in
data
.
keys
()
or
"cutoff"
not
in
data
.
keys
()
or
"bias_negative"
not
in
data
.
keys
()
or
"bias_positive"
not
in
data
.
keys
():
raise
ValueError
(
"Missing fields for TrustAdapter!"
)
return
TrustAdapter
(
data
[
"use_case"
],
data
[
"context"
],
data
[
"table"
],
data
[
"user_source"
],
data
[
"volume_source"
],
data
[
"rating_source"
],
data
[
"rating_lower"
],
data
[
"rating_upper"
],
data
[
"conversion"
],
data
[
"certainty"
],
data
[
"cutoff"
],
data
[
"bias_negative"
],
data
[
"bias_positive"
],
)
src/data-hub/reputation-calculation-microservice/app/lib/database/entities/trust_trace.py
0 → 100644
View file @
15615084
from
datetime
import
datetime
from
lib.trust.transaction
import
Transaction
from
typing
import
Dict
class
TrustTrace
(
Transaction
):
def
__init__
(
self
,
use_case
:
str
,
context
:
str
,
user
:
str
,
table
:
str
,
volume
:
float
,
timestamp
:
datetime
,
is_trustworthy
:
float
=
1
):
super
()
.
__init__
(
timestamp
,
volume
,
is_trustworthy
=
is_trustworthy
)
self
.
use_case
=
use_case
self
.
context
=
context
self
.
user
=
user
self
.
table
=
table
def
to_serializable_dict
(
self
):
return
{
"volume"
:
self
.
volume
,
"trustworthy"
:
self
.
trustworthy
,
"timestamp"
:
self
.
timestamp
.
isoformat
(),
"use_case"
:
self
.
use_case
,
"context"
:
self
.
context
,
"user"
:
self
.
user
,
"table"
:
self
.
table
,
}
@
staticmethod
def
from_serializable_dict
(
data
:
Dict
):
if
"volume"
not
in
data
.
keys
()
or
"trustworthy"
not
in
data
.
keys
()
or
"timestamp"
not
in
data
.
keys
()
or
"use_case"
not
in
data
.
keys
()
or
"context"
not
in
data
.
keys
()
or
"user"
not
in
data
.
keys
()
or
"table"
not
in
data
.
keys
():
raise
ValueError
(
"Missing fields for User!"
)
return
TrustTrace
(
data
[
"use_case"
],
data
[
"context"
],
data
[
"table"
],
data
[
"volume"
],
datetime
.
fromisoformat
(
data
[
"timestamp"
]),
is_trustworthy
=
data
[
"trustworthy"
]
)
src/data-hub/reputation-calculation-microservice/app/lib/database/entities/user.py
0 → 100644
View file @
15615084
from
lib.database.entities.user_trust
import
UserTrust
from
datetime
import
datetime
from
typing
import
Dict
,
List
class
User
:
def
__init__
(
self
,
use_case
:
str
,
name
:
str
,
context_trust
:
List
[
UserTrust
]
=
[]):
self
.
use_case
=
use_case
self
.
name
=
name
self
.
context_trust
=
context_trust
def
to_serializable_dict
(
self
,
include_trust
:
bool
=
False
):
if
include_trust
:
return
{
"use_case"
:
self
.
use_case
,
"name"
:
self
.
name
,
"context_trust"
:
[
t
.
to_serializable_dict
()
for
t
in
self
.
context_trust
]
}
else
:
return
{
"use_case"
:
self
.
use_case
,
"name"
:
self
.
name
}
@
staticmethod
def
from_serializable_dict
(
data
:
Dict
):
if
"use_case"
not
in
data
.
keys
()
or
"name"
not
in
data
.
keys
():
raise
ValueError
(
"Missing fields for User!"
)
return
User
(
data
[
"use_case"
],
data
[
"name"
])
src/data-hub/reputation-calculation-microservice/app/lib/database/entities/user_trust.py
0 → 100644
View file @
15615084
from
datetime
import
datetime
from
typing
import
Dict
class
UserTrust
:
def
__init__
(
self
,
use_case
:
str
,
context
:
str
,
user
:
str
,
t
:
float
,
last_update
:
datetime
):
self
.
use_case
=
use_case
self
.
context
=
context
self
.
user
=
user
self
.
t
=
t
self
.
last_update
=
last_update
def
to_serializable_dict
(
self
):
return
{
"use_case"
:
self
.
use_case
,
"context"
:
self
.
context
,
"user"
:
self
.
user
,
"t"
:
self
.
t
,
"last_update"
:
self
.
last_update
}
@
staticmethod
def
from_serializable_dict
(
data
:
Dict
):
if
"use_case"
not
in
data
.
keys
()
or
"context"
not
in
data
.
keys
()
or
"user"
not
in
data
.
keys
()
or
"t"
not
in
data
.
keys
()
or
"last_update"
not
in
data
.
keys
():
raise
ValueError
(
"Missing fields for User!"
)
return
UserTrust
(
data
[
"use_case"
],
data
[
"context"
],
data
[
"user"
],
data
[
"t"
],
data
[
"last_update"
])
\ No newline at end of file
src/data-hub/reputation-calculation-microservice/app/lib/database/repositories/context_repository.py
0 → 100644
View file @
15615084
# global imports (dont't worry, red is normal)
import
network_constants
as
netconst
from
database.MongoRepositoryBase
import
MongoRepositoryBase
from
lib.database.entities.context
import
Context
from
typing
import
List
,
Dict
class
ContextRepository
(
MongoRepositoryBase
):
'''This is a repository for MongoDb.'''
def
__init__
(
self
):
super
()
.
__init__
(
netconst
.
REPUTATION_CALCULATION_DB_HOSTNAME
,
netconst
.
REPUTATION_CALCULATION_DB_PORT
,
'reputation-computation-db'
)
self
.
_collection
=
'context'
def
add
(
self
,
context
:
Context
):
super
()
.
insert_entry
(
self
.
_collection
,
context
.
to_serializable_dict
())
def
all
(
self
)
->
List
[
Dict
]:
result
=
super
()
.
get_entries
(
self
.
_collection
,
projection
=
{
'_id'
:
False
})
return
list
(
result
)
def
all_for_use_case
(
self
,
use_case
:
str
)
->
List
[
Dict
]:
result
=
super
()
.
get_entries
(
self
.
_collection
,
projection
=
{
'_id'
:
False
},
selection
=
{
"use_case"
:
use_case
})
return
list
(
result
)
\ No newline at end of file
src/data-hub/reputation-calculation-microservice/app/lib/database/repositories/trust_adapter_repository.py
0 → 100644
View file @
15615084
# global imports (dont't worry, red is normal)
import
network_constants
as
netconst
from
database.MongoRepositoryBase
import
MongoRepositoryBase
from
lib.database.entities.trust_adapter
import
TrustAdapter
from
typing
import
List
,
Dict
class
TrustAdapterRepository
(
MongoRepositoryBase
):
'''This is a repository for MongoDb.'''
def
__init__
(
self
):
super
()
.
__init__
(
netconst
.
REPUTATION_CALCULATION_DB_HOSTNAME
,
netconst
.
REPUTATION_CALCULATION_DB_PORT
,
'reputation-computation-db'
)
self
.
_collection
=
'trust_adapter'
def
add
(
self
,
adapter
:
TrustAdapter
):
super
()
.
insert_entry
(
self
.
_collection
,
adapter
.
to_serializable_dict
())
def
all
(
self
)
->
List
[
Dict
]:
result
=
super
()
.
get_entries
(
self
.
_collection
,
projection
=
{
'_id'
:
False
})
return
list
(
result
)
def
all_for_use_case_and_context
(
self
,
use_case
:
str
,
context
:
str
)
->
List
[
Dict
]:
result
=
super
()
.
get_entries
(
self
.
_collection
,
projection
=
{
'_id'
:
False
},
selection
=
{
"use_case"
:
use_case
,
"context"
:
context
}
)
return
list
(
result
)
def
all_for_use_case_and_table
(
self
,
use_case
:
str
,
table
:
str
)
->
List
[
Dict
]:
result
=
super
()
.
get_entries
(
self
.
_collection
,
projection
=
{
'_id'
:
False
},
selection
=
{
"use_case"
:
use_case
,
"table"
:
table
}
)
return
list
(
result
)
def
one_for_use_case_and_context_and_table
(
self
,
use_case
:
str
,
context
:
str
,
table
:
str
)
->
Dict
:
result
=
super
()
.
get_entries
(
self
.
_collection
,
projection
=
{
'_id'
:
False
},
selection
=
{
"use_case"
:
use_case
,
"context"
:
context
,
"table"
:
table
}
)
result
=
list
(
result
)
if
len
(
result
)
!=
1
:
return
None
return
result
[
0
]
\ No newline at end of file
src/data-hub/reputation-calculation-microservice/app/lib/database/repositories/trust_trace_repository.py
0 → 100644
View file @
15615084
# global imports (dont't worry, red is normal)
import
network_constants
as
netconst
from
database.MongoRepositoryBase
import
MongoRepositoryBase
from
lib.database.entities.trust_trace
import
TrustTrace
from
typing
import
List
,
Dict
class
TrustTraceRepository
(
MongoRepositoryBase
):
'''This is a repository for MongoDb.'''
def
__init__
(
self
):
super
()
.
__init__
(
netconst
.
REPUTATION_CALCULATION_DB_HOSTNAME
,
netconst
.
REPUTATION_CALCULATION_DB_PORT
,
'reputation-computation-db'
)
self
.
_collection
=
'trust_trace'
def
add
(
self
,
trace
:
TrustTrace
):
super
()
.
insert_entry
(
self
.
_collection
,
trace
.
to_serializable_dict
())
def
all
(
self
)
->
List
[
Dict
]:
result
=
super
()
.
get_entries
(
self
.
_collection
,
projection
=
{
'_id'
:
False
})
return
list
(
result
)
def
all_for_use_case_and_context
(
self
,
use_case
:
str
,
context
:
str
)
->
List
[
Dict
]:
result
=
super
()
.
get_entries
(
self
.
_collection
,
projection
=
{
'_id'
:
False
},
selection
=
{
"use_case"
:
use_case
,
"context"
:
context
}
)
return
list
(
result
)
src/data-hub/reputation-calculation-microservice/app/lib/database/repositories/user_repository.py
0 → 100644
View file @
15615084
# global imports (dont't worry, red is normal)
import
network_constants
as
netconst
from
database.MongoRepositoryBase
import
MongoRepositoryBase
from
lib.database.entities.user
import
User
from
lib.database.entities.user_trust
import
UserTrust
from
lib.database.repositories.user_trust_repository
import
UserTrustRepository
from
typing
import
List
,
Dict
class
UserRepository
(
MongoRepositoryBase
):
'''This is a repository for MongoDb.'''
_user_trust_repository
=
UserTrustRepository
()
def
__init__
(
self
):
super
()
.
__init__
(
netconst
.
REPUTATION_CALCULATION_DB_HOSTNAME
,
netconst
.
REPUTATION_CALCULATION_DB_PORT
,
'reputation-computation-db'
)
self
.
_collection
=
'user'
def
add
(
self
,
user
:
User
):
super
()
.
insert_entry
(
self
.
_collection
,
user
.
to_serializable_dict
())
def
all
(
self
)
->
List
[
User
]:
result
=
super
()
.
get_entries
(
self
.
_collection
,
projection
=
{
'_id'
:
False
})
result
=
[
User
.
from_serializable_dict
(
row
)
for
row
in
list
(
result
)]
for
user
in
result
:
self
.
_enrich_user
(
user
)
return
result
def
all_for_use_case
(
self
,
use_case
:
str
)
->
List
[
User
]:
result
=
super
()
.
get_entries
(
self
.
_collection
,
projection
=
{
'_id'
:
False
},
selection
=
{
"use_case"
:
use_case
})
result
=
[
User
.
from_serializable_dict
(
row
)
for
row
in
list
(
result
)]
for
user
in
result
:
self
.
_enrich_user
(
user
)
return
result
def
one_by_use_case_and_username
(
self
,
use_case
:
str
,
username
:
str
)
->
User
:
result
=
list
(
super
()
.
get_entries
(
self
.
_collection
,
projection
=
{
'_id'
:
False
},
selection
=
{
"use_case"
:
use_case
,
"name"
:
username
}))
if
len
(
result
)
!=
1
:
return
None
user
:
User
=
User
.
from_serializable_dict
(
result
[
0
])
self
.
_enrich_user
(
user
)
return
user
def
_enrich_user
(
self
,
user
:
User
)
->
User
:
user
.
context_trust
=
self
.
_user_trust_repository
.
all_for_user
(
user
)
src/data-hub/reputation-calculation-microservice/app/lib/database/repositories/user_trust_repository.py
0 → 100644
View file @
15615084
# global imports (dont't worry, red is normal)
import
network_constants
as
netconst
from
database.MongoRepositoryBase
import
MongoRepositoryBase
from
lib.database.entities.user
import
User
from
lib.database.entities.user_trust
import
UserTrust
from
typing
import
List
,
Dict
class
UserTrustRepository
(
MongoRepositoryBase
):
'''This is a repository for MongoDb.'''
def
__init__
(
self
):
super
()
.
__init__
(
netconst
.
REPUTATION_CALCULATION_DB_HOSTNAME
,
netconst
.
REPUTATION_CALCULATION_DB_PORT
,
'reputation-computation-db'
)
self
.
_collection
=
'user_trust'
def
add
(
self
,
user_trust
:
UserTrust
):
super
()
.
insert_entry
(
self
.
_collection
,
user_trust
.
to_serializable_dict
())
def
all
(
self
)
->
List
[
UserTrust
]:
result
=
super
()
.
get_entries
(
self
.
_collection
,
projection
=
{
'_id'
:
False
})
return
[
UserTrust
.
from_serializable_dict
(
row
)
for
row
in
list
(
result
)]
def
all_for_use_case
(
self
,
use_case
:
str
)
->
List
[
UserTrust
]:
result
=
super
()
.
get_entries
(
self
.
_collection
,
projection
=
{
'_id'
:
False
},
selection
=
{
"use_case"
:
use_case
})
return
[
UserTrust
.
from_serializable_dict
(
row
)
for
row
in
list
(
result
)]
def
all_for_user
(
self
,
user
:
User
)
->
List
[
UserTrust
]:
result
=
super
()
.
get_entries
(
self
.
_collection
,
projection
=
{
'_id'
:
False
},
selection
=
{
"use_case"
:
user
.
use_case
,
"user"
:
user
.
name
})
return
[
UserTrust
.
from_serializable_dict
(
row
)
for
row
in
list
(
result
)]
def
one_for_user_and_context
(
self
,
user
:
User
,
context
:
str
)
->
UserTrust
:
result
=
super
()
.
get_entries
(
self
.
_collection
,
projection
=
{
'_id'
:
False
},
selection
=
{
"use_case"
:
user
.
use_case
,
"user"
:
user
.
name
,
"context"
:
context
})
result
=
list
(
result
)
if
len
(
result
)
!=
1
:
return
None
return
UserTrust
.
from_serializable_dict
(
result
[
0
])
def
update
(
self
,
user_trust
:
UserTrust
):
collection
=
self
.
_database
[
self
.
_collection
]
collection
.
update_one
({
"use_case"
:
user_trust
.
use_case
,
"context"
:
user_trust
.
context
,
"user"
:
user_trust
.
user
},
{
"$set"
:
user_trust
.
to_serializable_dict
()})
src/data-hub/reputation-calculation-microservice/app/lib/routes/context.py
0 → 100644
View file @
15615084
from
lib.database.entities.context
import
Context
from
lib.database.repositories.context_repository
import
ContextRepository
from
flask
import
request
,
Response
context_repository
:
ContextRepository
=
ContextRepository
()
def
add
(
use_case
:
str
):
data
=
request
.
json
context_new
=
None
try
:
context_new
=
Context
.
from_serializable_dict
(
data
)
except
ValueError
:
return
Response
(
status
=
400
,
response
=
"Missing fields in request"
)
if
context_new
.
use_case
!=
use_case
:
return
Response
(
status
=
400
,
response
=
"Use-Cases in body and url do not match!"
)
context_repository
.
add
(
context_new
)
return
context_new
.
to_serializable_dict
()
def
all_for_use_case
(
use_case
:
str
):
return
context_repository
.
all_for_use_case
(
use_case
)
src/data-hub/reputation-calculation-microservice/app/lib/routes/traces.py
0 → 100644
View file @
15615084
from
lib.services.trust_service
import
TrustService
from
typing
import
Dict
from
flask
import
request
,
Response
from
lib.database.repositories.trust_adapter_repository
import
TrustAdapterRepository
from
lib.database.repositories.user_repository
import
UserRepository
from
lib.database.repositories.user_trust_repository
import
UserTrustRepository
from
lib.database.repositories.trust_trace_repository
import
TrustTraceRepository
trust_service
:
TrustService
=
TrustService
(
TrustAdapterRepository
(),
UserRepository
(),
UserTrustRepository
(),
TrustTraceRepository
()
)
def
add
(
use_case
:
str
,
table
:
str
):
data
:
Dict
=
request
.
json
trust_service
.
updateSystem
(
use_case
,
table
,
data
[
"properties"
])
\ No newline at end of file
src/data-hub/reputation-calculation-microservice/app/lib/routes/trust_adapter.py
0 → 100644
View file @
15615084
from
lib.database.repositories.context_repository
import
ContextRepository
from
lib.database.entities.context
import
Context
from
lib.database.entities.trust_adapter
import
TrustAdapter
from
lib.database.repositories.context_repository
import
ContextRepository
from
lib.database.repositories.trust_adapter_repository
import
TrustAdapterRepository
from
flask
import
request
,
Response
from
typing
import
List
context_repository
:
ContextRepository
=
ContextRepository
()
trust_adapter_repository
:
TrustAdapterRepository
=
TrustAdapterRepository
()
def
add
(
use_case
:
str
,
context
:
str
):
_ensure_context_exists_for_use_case
(
use_case
,
context
)
data
=
request
.
json
adapter_new
:
TrustAdapter
=
None
try
:
adapter_new
=
TrustAdapter
.
from_serializable_dict
(
data
)
except
ValueError
:
return
Response
(
status
=
400
,
response
=
"Missing fields in request"
)
if
adapter_new
.
context
!=
context
:
return
Response
(
status
=
400
,
response
=
"Context in body and url do not match!"
)
if
adapter_new
.
use_case
!=
use_case
:
return
Response
(
status
=
400
,
response
=
"Use-Cases in body and url do not match!"
)
adapter_reference
=
trust_adapter_repository
.
one_for_use_case_and_context_and_table
(
use_case
,
context
,
adapter_new
.
table
)
if
adapter_reference
!=
None
:
return
Response
(
status
=
400
,
response
=
"There already exists a TrustAdapter for this use-case/context/table"
)
trust_adapter_repository
.
add
(
adapter_new
)
return
adapter_new
.
to_serializable_dict
()
def
all_for_use_case_and_context
(
use_case
:
str
,
context
:
str
):
_ensure_context_exists_for_use_case
(
use_case
,
context
)
return
trust_adapter_repository
.
all_for_use_case_and_context
(
use_case
,
context
)
def
_ensure_context_exists_for_use_case
(
use_case
:
str
,
context
:
str
)
->
Context
:
contexts
:
List
[
Context
]
=
context_repository
.
all_for_use_case
(
use_case
)
target_context
=
None
for
c
in
contexts
:
if
c
[
"name"
]
==
context
:
target_context
=
c
break
if
target_context
==
None
:
return
Response
(
status
=
404
,
response
=
f
"Context {context} not found!"
)
return
Context
.
from_serializable_dict
(
target_context
)
src/data-hub/reputation-calculation-microservice/app/lib/routes/user.py
0 → 100644
View file @
15615084
from
lib.database.entities.user
import
User
from
lib.database.repositories.user_repository
import
UserRepository
from
flask
import
request
,
Response
user_repository
:
UserRepository
=
UserRepository
()
def
add
(
use_case
:
str
):
data
=
request
.
json
user_new
=
None
try
:
user_new
=
User
.
from_serializable_dict
(
data
)
except
ValueError
:
return
Response
(
status
=
400
,
response
=
"Missing fields in request"
)
if
user_new
.
use_case
!=
use_case
:
return
Response
(
status
=
400
,
response
=
"Use-Cases in body and url do not match!"
)
user_repository
.
add
(
user_new
)
return
user_new
.
to_serializable_dict
()
def
all_for_use_case
(
use_case
:
str
):
return
[
user
.
to_serializable_dict
(
include_trust
=
True
)
for
user
in
user_repository
.
all_for_use_case
(
use_case
)]
src/data-hub/reputation-calculation-microservice/app/lib/services/trust_service.py
0 → 100644
View file @
15615084
from
lib.database.entities.trust_trace
import
TrustTrace
from
lib.database.entities.user_trust
import
UserTrust
from
lib.trust.oracle
import
TrustOracle
from
lib.database.entities.trust_adapter
import
TrustAdapter
from
lib.database.entities.user
import
User
from
typing
import
Dict
,
List
import
logging
from
datetime
import
datetime
LOGGER
=
logging
.
getLogger
(
__name__
)
class
TrustService
:
def
__init__
(
self
,
adapter_repository
,
user_repository
,
user_trust_repository
,
trust_trace_repository
):
self
.
_adapter_repository
=
adapter_repository
self
.
_user_repository
=
user_repository
self
.
_user_trust_repository
=
user_trust_repository
self
.
_trust_trace_repository
=
trust_trace_repository
def
updateSystem
(
self
,
use_case
:
str
,
table
:
str
,
properties
:
Dict
):
adapters
:
List
[
TrustAdapter
]
=
[
TrustAdapter
.
from_serializable_dict
(
row
)
for
row
in
self
.
_adapter_repository
.
all_for_use_case_and_table
(
use_case
,
table
)]
if
len
(
adapters
)
==
0
:
LOGGER
.
warning
(
f
"No Trust-Adapters found for Trace(use-case:{use_case}, table:{table}), aborting..."
)
return
for
adapter
in
adapters
:
if
adapter
.
user_source
not
in
properties
.
keys
()
or
adapter
.
volume_source
not
in
properties
.
keys
()
or
adapter
.
rating_source
not
in
properties
.
keys
():
raise
ValueError
(
f
"Trace does not contain needed fields for trust-computation. Required fields: {adapter.user_source}, {adapter.volume_source}. Present fields: {properties.keys()}"
)
username
:
str
=
properties
[
adapter
.
user_source
]
volume
:
float
=
properties
[
adapter
.
volume_source
]
rating
:
float
=
properties
[
adapter
.
rating_source
]
# retrieve user (create one if it doesnt exist)
user
:
User
=
self
.
_user_repository
.
one_by_use_case_and_username
(
use_case
,
username
)
if
user
==
None
:
user
=
User
(
use_case
,
username
)
self
.
_user_repository
.
add
(
user
)
trace
:
TrustTrace
=
TrustTrace
(
use_case
,
adapter
.
context
,
user
.
name
,
adapter
.
table
,
volume
,
datetime
.
now
(),
is_trustworthy
=
(((
rating
-
adapter
.
rating_lower
)
/
(
adapter
.
rating_upper
-
adapter
.
rating_lower
))
*
2
)
-
1
)
# retrieve user_trust for the trace (create one if it doesnt exist)
target_trust
:
UserTrust
=
None
for
user_trust
in
user
.
context_trust
:
if
user_trust
.
context
==
adapter
.
context
:
target_trust
=
user_trust
break
if
target_trust
==
None
:
target_trust
=
UserTrust
(
use_case
,
adapter
.
context
,
user
.
name
,
0
,
datetime
(
1
,
1
,
1
,
0
,
0
))
self
.
_user_trust_repository
.
add
(
target_trust
)
user
.
context_trust
.
append
(
target_trust
)
oracle
:
TrustOracle
=
TrustOracle
(
adapter
.
conversion
,
adapter
.
certainty
)
delta_t
=
oracle
.
calculate_trust_single
(
trace
)
target_trust
.
t
+=
delta_t
target_trust
.
last_update
=
trace
.
timestamp
self
.
_trust_trace_repository
.
add
(
trace
)
self
.
_user_trust_repository
.
update
(
target_trust
)
src/data-hub/reputation-calculation-microservice/app/lib/trust/message_handler.py
0 → 100644
View file @
15615084
from
lib.services.trust_service
import
TrustService
from
typing
import
Dict
import
json
import
logging
LOGGER
=
logging
.
getLogger
(
__name__
)
class
MessageHandler
:
def
__init__
(
self
,
trust_adapter_repository
,
user_repository
,
user_trust_repository
,
trust_trace_repository
):
self
.
trust_adapter_repository
=
trust_adapter_repository
self
.
user_repository
=
user_repository
self
.
user_trust_repository
=
user_trust_repository
self
.
trust_trace_repository
=
trust_trace_repository
def
handle
(
self
,
body
:
str
):
# decode the message
data
:
Dict
=
None
try
:
data
=
json
.
loads
(
body
)
except
json
.
decoder
.
JSONDecodeError
:
raise
ValueError
(
"Invalid Message: Not in JSON format"
)
if
"type"
not
in
data
.
keys
()
or
"content"
not
in
data
.
keys
():
raise
ValueError
(
"Invalid Message: Missing fields
\"
type
\"
or
\"
content
\"
"
)
if
data
[
"type"
]
!=
"new-trace"
:
return
content
:
Dict
=
data
[
"content"
]
if
"use_case"
not
in
content
.
keys
()
or
"table"
not
in
content
.
keys
()
or
"properties"
not
in
content
.
keys
():
raise
ValueError
(
"Invalid Message: Missing fields
\"
use_case
\"
or
\"
table
\"
or
\"
properties
\"
"
)
use_case
:
str
=
content
[
"use_case"
]
table
:
str
=
content
[
"table"
]
properties
:
Dict
=
content
[
"properties"
]
trust_service
:
TrustService
=
TrustService
(
self
.
trust_adapter_repository
,
self
.
user_repository
,
self
.
user_trust_repository
,
self
.
trust_trace_repository
)
trust_service
.
updateSystem
(
use_case
,
table
,
properties
)
\ No newline at end of file
src/data-hub/reputation-calculation-microservice/app/lib/trust/node.py
0 → 100644
View file @
15615084
class
Node
:
def
__init__
(
self
,
initial_t
=
0
):
self
.
t
=
initial_t
\ No newline at end of file
src/data-hub/reputation-calculation-microservice/app/lib/trust/oracle.py
0 → 100644
View file @
15615084
import
math
from
enum
import
Enum
from
lib.trust.transaction
import
Transaction
from
lib.trust.node
import
Node
from
datetime
import
datetime
from
typing
import
List
,
Dict
class
TrustLabel
(
Enum
):
UNTRUSTWORTHY
=
1
UNSURE
=
2
TRUSTWORTHY
=
3
class
TrustOracle
:
def
__init__
(
self
,
translate_value
:
float
,
knowledge_border
:
float
):
# translates transaction volume into unitless x-axis value for sigmoid input
self
.
translate_value
=
translate_value
# nodes must reach a value of at least |trust(node)| = knowledge_border to get a definite label
self
.
knowledge_border
=
knowledge_border
def
rate_node
(
self
,
node
:
Node
)
->
TrustLabel
:
if
node
.
t
<
-
self
.
knowledge_border
:
return
TrustLabel
.
UNTRUSTWORTHY
if
node
.
t
>
self
.
knowledge_border
:
return
TrustLabel
.
TRUSTWORTHY
return
TrustLabel
.
UNSURE
def
calculate_trust_single
(
self
,
node_transaction
:
Transaction
):
return
self
.
calculate_trust
([
node_transaction
])
def
calculate_trust
(
self
,
node_transactions
:
List
[
Transaction
]):
trust_total
=
0
for
transaction
in
node_transactions
:
transaction_t
=
abs
(
transaction
.
volume
/
self
.
translate_value
)
trust_total
+=
transaction_t
return
self
.
translate_t_to_trust_value
(
trust_total
)
def
translate_t_to_trust_value
(
self
,
t_value
:
float
):
# classic sigmoid function as in https://en.wikipedia.org/wiki/Sigmoid_function
sig
=
1
/
(
1
+
math
.
exp
(
-
t_value
))
# rescale it st.:
# values are between ]-1;1[
sig
*=
2
sig
-=
1
return
sig
src/data-hub/reputation-calculation-microservice/app/lib/trust/transaction.py
0 → 100644
View file @
15615084
class
Transaction
:
def
__init__
(
self
,
timestamp
,
volume
=
1
,
is_trustworthy
:
float
=
1
):
self
.
volume
=
volume
self
.
trustworthy
=
is_trustworthy
self
.
timestamp
=
timestamp
src/data-hub/reputation-calculation-microservice/app/main.py
View file @
15615084
# add modules folder to interpreter path
import
sys
import
os
from
pathlib
import
Path
modules_path
=
'../../../modules/'
if
os
.
path
.
exists
(
modules_path
):
sys
.
path
.
insert
(
1
,
modules_path
)
# init logging to file
import
logging
LOG_FORMAT
=
(
'
%(levelname) -5
s
%(asctime)
s
%(name)
s:
%(funcName) -35
s
%(lineno) -5
d:
%(message)
s'
)
logging
.
basicConfig
(
level
=
logging
.
INFO
,
format
=
LOG_FORMAT
)
LOGGER
=
logging
.
getLogger
(
__name__
)
#############################
import
connexion
from
security
import
swagger_util
from
env_info
import
is_running_locally
,
get_resources_path
from
flask
import
request
from
flask
import
redirect
from
flask_cors
import
CORS
# load swagger config
# load swagger config
app
=
connexion
.
App
(
__name__
,
specification_dir
=
'configs/'
)
app
.
add_api
(
'swagger.yml'
)
CORS
(
app
.
app
)
@
app
.
app
.
before_request
def
before_request
():
if
request
.
url
.
startswith
(
'http://'
):
url
=
request
.
url
.
replace
(
'http://'
,
'https://'
,
1
)
code
=
301
return
redirect
(
url
,
code
=
code
)
@
app
.
route
(
'/'
,
methods
=
[
'GET'
])
def
api_root
():
return
'Endpoint of reputation-calculation-microservice!'
return
redirect
(
'/api/ui'
)
# SSL configuration
certificate_path
=
get_resources_path
()
context
=
(
os
.
path
.
normpath
(
f
'{certificate_path}/articonf1.crt'
),
os
.
path
.
normpath
(
f
'{certificate_path}/articonf1.key'
))
# certificate and key files
if
is_running_locally
():
print
(
"Running locally..."
)
app
.
add_api
(
swagger_util
.
get_bundled_specs
(
Path
(
"configs/swagger_local.yml"
)),
resolver
=
connexion
.
RestyResolver
(
"cms_rest_api"
))
else
:
app
.
add_api
(
swagger_util
.
get_bundled_specs
(
Path
(
"configs/swagger.yml"
)),
resolver
=
connexion
.
RestyResolver
(
"cms_rest_api"
))
# start app
if
__name__
==
'__main__'
:
app
.
run
(
host
=
'0.0.0.0'
,
port
=
5000
,
debug
=
True
)
app
.
run
(
host
=
'0.0.0.0'
,
port
=
5000
,
debug
=
True
,
use_reloader
=
False
,
ssl_context
=
context
)
# disable reloader so only subscribed once to rabbitmq
src/data-hub/reputation-calculation-microservice/app/requirements.txt
0 → 100644
View file @
15615084
B
attrs==21.2.0
src/data-hub/reputation-calculation-microservice/app/tests/test_messaging.py
0 → 100644
View file @
15615084
from
lib.database.entities.trust_trace
import
TrustTrace
from
lib.database.entities.user_trust
import
UserTrust
from
lib.database.entities.user
import
User
from
lib.database.entities.trust_adapter
import
TrustAdapter
from
lib.trust.message_handler
import
MessageHandler
from
typing
import
List
,
Dict
import
unittest
import
json
class
DummyTrustAdapterRepo
:
_adapters
:
List
[
TrustAdapter
]
=
[
TrustAdapter
(
"car-sharing"
,
"context1"
,
"travel"
,
"user"
,
"cost"
,
"rating"
,
1
,
5
,
2000
,
0.75
,
8
,
1
,
1
)
]
def
add
(
self
,
adapter
:
TrustAdapter
):
self
.
_adapters
.
append
(
adapter
)
def
all
(
self
)
->
List
[
Dict
]:
return
self
.
_adapters
def
all_for_use_case_and_context
(
self
,
use_case
:
str
,
context
:
str
)
->
List
[
Dict
]:
return
[
adapter
.
to_serializable_dict
()
for
adapter
in
self
.
_adapters
if
(
adapter
.
use_case
==
use_case
and
adapter
.
context
==
context
)]
def
all_for_use_case_and_table
(
self
,
use_case
:
str
,
table
:
str
)
->
List
[
Dict
]:
return
[
adapter
.
to_serializable_dict
()
for
adapter
in
self
.
_adapters
if
(
adapter
.
use_case
==
use_case
and
adapter
.
table
==
table
)]
def
one_for_use_case_and_context_and_table
(
self
,
use_case
:
str
,
context
:
str
,
table
:
str
)
->
Dict
:
result
=
[
adapter
for
adapter
in
self
.
_adapters
if
(
adapter
.
use_case
==
use_case
and
adapter
.
context
==
context
and
adapter
.
table
==
table
)]
if
len
(
result
)
!=
1
:
return
None
return
result
[
0
]
.
to_serializable_dict
()
class
DummyUserRepo
:
_users
:
List
[
User
]
=
[]
def
clear
(
self
):
self
.
_users
.
clear
()
def
add
(
self
,
user
:
User
):
self
.
_users
.
append
(
user
)
def
all
(
self
)
->
List
[
User
]:
return
self
.
_users
def
all_for_use_case
(
self
,
use_case
:
str
)
->
List
[
User
]:
return
[
user
for
user
in
self
.
_users
if
(
user
.
use_case
==
use_case
)]
def
one_by_use_case_and_username
(
self
,
use_case
:
str
,
username
:
str
)
->
User
:
result
=
[
user
for
user
in
self
.
_users
if
(
user
.
use_case
==
use_case
and
user
.
name
==
username
)]
if
len
(
result
)
!=
1
:
return
None
return
result
[
0
]
class
DummyUserTrustRepo
:
_user_trusts
:
List
[
UserTrust
]
=
[]
def
add
(
self
,
user_trust
:
UserTrust
):
self
.
_user_trusts
.
append
(
user_trust
)
def
all
(
self
)
->
List
[
UserTrust
]:
return
self
.
_user_trusts
def
all_for_use_case
(
self
,
use_case
:
str
)
->
List
[
UserTrust
]:
return
[
user_trust
for
user_trust
in
self
.
_user_trusts
if
(
user_trust
.
use_case
==
use_case
)]
def
all_for_user
(
self
,
user
:
User
)
->
List
[
UserTrust
]:
return
[
user_trust
for
user_trust
in
self
.
_user_trusts
if
(
user_trust
.
use_case
==
user
.
use_case
and
user_trust
.
user
==
user
.
name
)]
def
one_for_user_and_context
(
self
,
user
:
User
,
context
:
str
)
->
UserTrust
:
result
=
[
user_trust
for
user_trust
in
self
.
user_trust
if
(
user_trust
.
use_case
==
user
.
use_case
and
user_trust
.
name
==
user
.
name
and
user_trust
.
context
==
context
)]
if
len
(
result
)
!=
1
:
return
None
return
result
[
0
]
def
update
(
self
,
user_trust
:
UserTrust
):
pass
class
DummyTrustTraceRepository
:
_traces
:
List
[
TrustTrace
]
=
[]
def
add
(
self
,
trace
:
TrustTrace
):
self
.
_traces
.
append
(
trace
)
def
all
(
self
)
->
List
[
Dict
]:
return
[
trace
.
to_serializable_dict
()
for
trace
in
self
.
_traces
]
def
all_for_use_case_and_context
(
self
,
use_case
:
str
,
context
:
str
)
->
List
[
Dict
]:
result
=
[
trace
for
trace
in
self
.
_traces
if
(
trace
.
use_case
==
use_case
and
trace
.
context
==
context
)]
return
[
trace
.
to_serializable_dict
()
for
trace
in
result
]
class
Test_Messages
(
unittest
.
TestCase
):
user_repo
=
None
message_handler
:
MessageHandler
=
None
def
setUp
(
self
):
self
.
user_repo
=
DummyUserRepo
()
self
.
message_handler
=
MessageHandler
(
DummyTrustAdapterRepo
(),
self
.
user_repo
,
DummyUserTrustRepo
(),
DummyTrustTraceRepository
()
)
def
_buildTrace
(
self
):
return
json
.
dumps
({
"type"
:
"new-trace"
,
"content"
:
{
"use_case"
:
"car-sharing"
,
"table"
:
"travel"
,
"id"
:
"092870924809481f"
,
"properties"
:
{
"user"
:
"Bob"
,
"cost"
:
201.12
,
"rating"
:
4
}
}
})
def
test_message_no_json
(
self
):
message
:
str
=
"abcdefg"
try
:
self
.
message_handler
.
handle
(
message
)
except
ValueError
:
pass
def
test_message_valid
(
self
):
message
=
self
.
_buildTrace
()
self
.
user_repo
.
clear
()
self
.
assertEqual
(
0
,
len
(
self
.
user_repo
.
all
()))
self
.
message_handler
.
handle
(
message
)
self
.
assertEqual
(
1
,
len
(
self
.
user_repo
.
all
()))
# ensure user was added
if
__name__
==
'__main__'
:
unittest
.
main
()
src/data-hub/reputation-calculation-microservice/deployment/deployment.yml
View file @
15615084
...
...
@@ -31,4 +31,58 @@ spec:
-
name
:
reputation-calculation
image
:
alexx882/reputation-calculation-microservice
ports
:
-
containerPort
:
5000
\ No newline at end of file
-
containerPort
:
5000
imagePullPolicy
:
Always
volumeMounts
:
-
mountPath
:
/srv/articonf
name
:
articonf
volumes
:
-
name
:
articonf
hostPath
:
path
:
/srv/articonf
type
:
Directory
---
apiVersion
:
v1
kind
:
Service
metadata
:
name
:
reputation-calculation-db
spec
:
type
:
LoadBalancer
selector
:
app
:
reputation-calculation-db
ports
:
-
name
:
http
port
:
27017
targetPort
:
27017
nodePort
:
30109
protocol
:
TCP
---
apiVersion
:
apps/v1
kind
:
Deployment
metadata
:
name
:
reputation-calculation-db
spec
:
replicas
:
1
selector
:
matchLabels
:
app
:
reputation-calculation-db
template
:
metadata
:
labels
:
app
:
reputation-calculation-db
spec
:
containers
:
-
name
:
reputation-calculation-db
image
:
mongo
env
:
-
name
:
MONGO_INITDB_ROOT_USERNAME
value
:
root
-
name
:
MONGO_INITDB_ROOT_PASSWORD
value
:
root
ports
:
-
containerPort
:
27017
volumeMounts
:
-
mountPath
:
/data/db
name
:
dbdata
volumes
:
-
name
:
dbdata
src/modules/network_constants.py
View file @
15615084
...
...
@@ -111,4 +111,16 @@ else:
FEDERATED_TRAINING_REST_PORT
=
30424
#FEDERATED_TRAINING_DB_PORT = 30425
#endregion Federated Training
\ No newline at end of file
#endregion Federated Training
## Reputation-Calculation
if
server
:
REPUTATION_CALCULATION_HOSTNAME
=
'reputation-calculation'
REPUTATION_CALCULATION_DB_HOSTNAME
=
f
'{BUSINESS_LOGIC_HOSTNAME}-db'
REPUTATION_CALCULATION_REST_PORT
=
80
REPUTATION_CALCULATION_DB_PORT
=
27017
else
:
REPUTATION_CALCULATION_HOSTNAME
=
'articonf1.itec.aau.at'
REPUTATION_CALCULATION_DB_HOSTNAME
=
'articonf1.itec.aau.at'
REPUTATION_CALCULATION_REST_PORT
=
30107
REPUTATION_CALCULATION_DB_PORT
=
30109
#endregion Reputation-Calculation
src/rest-gateway/Dockerfile
View file @
15615084
...
...
@@ -2,15 +2,18 @@ FROM python:3
LABEL
maintainer="Alexander Lercher"
RUN
apt-get update
RUN
pip
install
flask
RUN
pip
install
connexion[swagger-ui]
EXPOSE
5000
WORKDIR
/app
COPY
src/
rest-gateway
/app/requirements.txt /app/
COPY
src/
data-hub/reputation-calculation-microservice
/app/requirements.txt /app/
RUN
pip
install
-r
requirements.txt
COPY
src/rest-gateway/app/ /app/
COPY
src/data-hub/reputation-calculation-microservice/app/ /app/
COPY
src/modules/ /app/
RUN
chmod
a+x main.py
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment