Commit 8a5dcf17 authored by delanoe's avatar delanoe

[FEAT] Nodes of graphs need some label but clustering is ok (need to be done on map list only)

parent 05f9197a
from gargantext.models import Node, Ngram, NodeNgram, NodeNgramNgram, \
NodeHyperdata
from gargantext.util.db import session, aliased, bulk_insert, func
from gargantext.util.lists import WeightedMatrix, UnweightedList, Translations
from sqlalchemy import desc, asc, or_, and_
#import inspect
import datetime
def do_cooc(corpus=None
, field1='ngrams', field2='ngrams'
, mainList_id=None, stopList_id=None, groupList_id=None
, coocNode_id=None
, cvalue_id=None
, n_min=1, n_max=None
, start=None, end=None
, limit=1000
, isMonopartite=True
, threshold = 3):
'''
Compute the cooccurence matrix and save it, returning NodeNgramNgram.node_id
For the moment list of paramters are not supported because, lists need to
be merged before.
corpus :: Corpus
cvalue_id :: Int
mainList_id :: Int
stopList_id :: Int
groupList_id :: Int
For the moment, start and end are simple, only year is implemented yet
start :: TimeStamp -- example: '2010-05-30 02:00:00+02'
end :: TimeStamp
limit :: Int
'''
# TODO : add hyperdata here
# Security test
field1,field2 = str(field1), str(field2)
# Get node
if not coocNode_id:
coocNode_id0 = (session.query(Node.id).filter(Node.typename == "COOCCURRENCES"
, Node.name == "GRAPH EXPLORER"
, Node.parent_id == corpus.id
).first())
if not coocNode_id:
coocNode = corpus.add_child(
typename = "COOCCURRENCES",
name = "GRAPH EXPLORER COOC (in:%s)" % corpus.id
)
session.add(coocNode)
session.commit()
coocNode_id = coocNode.id
else :
coocNode_id = coocNode_id[0]
# node_cooc = get_or_create_node(nodetype='Cooccurrence', corpus=corpus
# , name_str="Cooccurrences corpus " \
# + str(corpus.id) + "list_id: " + str(mainList_id)
# #, hyperdata={'field1': field1, 'field2':field2}
# , session=session)
# BEGIN
# Saving the parameters of the analysis in the Node JSONB hyperdata field
# ok args, _, _, parameters = inspect.getargvalues(inspect.currentframe())
# hyperdata = dict()
#
# for parameter in parameters.keys():
# if parameter != 'corpus' and parameter != 'node_cooc':
# hyperdata[parameter] = parameters[parameter]
#
# node_cooc.hyperdata = hyperdata
#
# For tests only
session.query(NodeNgramNgram).filter(NodeNgramNgram.node_id==coocNode_id).delete()
session.commit()
NodeNgramX = aliased(NodeNgram)
cooc_score = func.count(NodeNgramX.node_id).label('cooc_score')
#cooc_score = func.sqrt(func.sum(NodeNgramX.weight * NodeNgramY.weight)).label('cooc_score')
#print([n for n in test_query])
if isMonopartite :
NodeNgramY = aliased(NodeNgram)
cooc_query = (session.query(NodeNgramX.ngram_id, NodeNgramY.ngram_id, cooc_score)
.join(Node, Node.id == NodeNgramX.node_id)
.join(NodeNgramY, NodeNgramY.node_id == Node.id)
.filter(Node.parent_id==corpus.id, Node.typename=="DOCUMENT")
)
else :
NodeNgramY = aliased(NodeNgram)
cooc_query = (session.query(NodeHyperdataNgram.ngram_id, NodeNgramY.ngram_id, cooc_score)
.join(Node, Node.id == NodeHyperdataNgram.node_id)
.join(NodeNgramY, NodeNgramY.node_id == Node.id)
.join(Hyperdata, Hyperdata.id == NodeHyperdataNgram.hyperdata_id)
.filter(Node.parent_id == corpus.id, Node.typename == "DOCUMENT")
.filter(Hyperdata.name == field1)
)
#print(cooc_query)
# Size of the ngrams between n_min and n_max
if n_min is not None or n_max is not None:
if isMonopartite:
NgramX = aliased(Ngram)
cooc_query = cooc_query.join(NgramX, NgramX.id == NodeNgramX.ngram_id)
NgramY = aliased(Ngram)
cooc_query = (cooc_query
.join(NgramY, NgramY.id == NodeNgramY.ngram_id)
)
if n_min is not None:
cooc_query = (cooc_query
.filter(NgramY.n >= n_min)
)
if isMonopartite:
cooc_query = cooc_query.filter(NgramX.n >= n_min)
if n_max is not None:
cooc_query = (cooc_query
.filter(NgramY.n >= n_min)
)
if isMonopartite:
cooc_query = cooc_query.filter(NgramX.n >= n_min)
# Cooc between the dates start and end
if start is not None:
#date_start = datetime.datetime.strptime ("2001-2-3 10:11:12", "%Y-%m-%d %H:%M:%S")
# TODO : more complexe date format here.
date_start = datetime.datetime.strptime (str(start), "%Y-%m-%d")
date_start_utc = date_start.strftime("%Y-%m-%d %H:%M:%S")
Start=aliased(NodeHyperdata)
StartFormat = aliased(Hyperdata)
cooc_query = (cooc_query.join(Start, Start.node_id == Node.id)
.join(StartFormat, StartFormat.id == Start.hyperdata_id)
.filter(StartFormat.name == 'publication_date')
.filter(Start.value_datetime >= date_start_utc)
)
if end is not None:
# TODO : more complexe date format here.
date_end = datetime.datetime.strptime (str(end), "%Y-%m-%d")
date_end_utc = date_end.strftime("%Y-%m-%d %H:%M:%S")
End=aliased(NodeHyperdata)
EndFormat = aliased(Hyperdata)
cooc_query = (cooc_query.join(End, End.node_id == Node.id)
.join(EndFormat, EndFormat.id == End.hyperdata_id)
.filter(EndFormat.name == 'publication_date')
.filter(End.value_datetime <= date_end_utc)
)
if isMonopartite:
# Cooc is symetric, take only the main cooccurrences and cut at the limit
cooc_query = cooc_query.filter(NodeNgramX.ngram_id < NodeNgramY.ngram_id)
cooc_query = cooc_query.having(cooc_score > threshold)
if isMonopartite:
cooc_query = cooc_query.group_by(NodeNgramX.ngram_id, NodeNgramY.ngram_id)
else:
cooc_query = cooc_query.group_by(NodeHyperdataNgram.ngram_id, NodeNgramY.ngram_id)
cooc_query = cooc_query.order_by(desc('cooc_score'))
# END of the query
matrix = WeightedMatrix(cooc_query)
#print(matrix)
# Select according some scores
if cvalue_id is not None :
#miam = get_or_create_node(nodetype='Cvalue', corpus=corpus)
cvalue_list = UnweightedList(session.query(NodeNodeNgram.ngram_id)
.filter(NodeNodeNgram.nodex_id == cvalue_id).all()
)
if isMonopartite:
if mainList_id is not None :
miam_list = UnweightedList(mainList_id)
if stopList_id is not None :
stop_list = UnweightedList(stopList_id)
if groupList_id is not None :
group_list = Translations(groupList_id)
if mainList_id is not None and stopList_id is None and groupList_id is None :
cooc = matrix & miam_list
elif mainList_id is not None and stopList_id is not None and groupList_id is None :
cooc = matrix & (miam_list - stop_list)
elif mainList_id is not None and stopList_id is not None and groupList_id is not None :
print("mainList_id is not None and stopList_id is not None and groupList_id is not None")
cooc = matrix & (miam_list * group_list - stop_list)
#cooc = matrix & (miam_list - stop_list)
elif mainList_id is not None and stopList_id is None and groupList_id is not None :
cooc = matrix & (miam_list * group_list)
else :
cooc = matrix
else:
cooc = matrix
cooc.save(coocNode_id)
return(coocNode_id)
......@@ -19,17 +19,17 @@ class Graph(APIView):
'''
# implicit global session
field1 = request.GET.get ('field1' , 'ngrams' )
field2 = request.GET.get ('field2' , 'ngrams' )
field1 = str(request.GET.get ('field1' , 'ngrams' ))
field2 = str(request.GET.get ('field2' , 'ngrams' ))
start = request.GET.get ('start' , None )
end = request.GET.get ('end' , None )
threshold = request.GET.get ('threshold' , 1 )
bridgeness = request.GET.get ('bridgeness', -1 )
format_ = request.GET.get ('format' , 'json' )
type_ = request.GET.get ('type' , 'node_link' )
distance = request.GET.get ('distance' , 'conditional')
threshold = int(request.GET.get ('threshold' , 1 ))
bridgeness = int(request.GET.get ('bridgeness', -1 ))
format_ = str(request.GET.get ('format' , 'json' ))
type_ = str(request.GET.get ('type' , 'node_link' ))
distance = str(request.GET.get ('distance' , 'conditional'))
corpus = session.query(Node).filter(Node.id==corpus_id).first()
......
<html>
<head>
{% load staticfiles %}
<link rel="stylesheet" href="{% static "css/bootstrap.css" %}">
<link rel="stylesheet" href="{% static "css/bootstrap-theme.min.css" %}">
<link rel="stylesheet" href="{% static "js/libs/jquery/jquery-ui.css" %}" media="screen">
<!-- <link rel="stylesheet" href="{% static "js/libs/bootstrap/css/bootstrap.css" %}" media="screen"> -->
<link rel="stylesheet" href="{% static "js/libs/css2/freshslider.css" %}" media="screen">
<link rel="stylesheet" href="{% static "js/libs/css2/custom.css" %}" media="screen">
<link rel="stylesheet" href="{% static "js/libs/css2/sidebar.css" %}" media="screen">
<style>
#leftcolumn {
font-size: 10px;
width:20%;
overflow: auto;
}
#ctlzoom {
width:7%;
}
#topPapers{
margin: 7px;
padding: 5px 0px 5px 5px;
}
#topPapers ul {
list-style-type: none;
}
#ctlzoom {
position: absolute; right: 16%; bottom: 1%; list-style: none; padding: 0; margin: 0;
}
#category0 ul li { margin: 0 12px 12px 0; }
#category1 ul li { margin: 0 12px 12px 0; }
</style>
<!--
<link rel="stylesheet" href="{% static "js/libs/bootswatch/css/bootswatch.css" %}">
<link rel="stylesheet" href="{% static "js/libs/css2/font.css" %}" type="text/css">
-->
</head>
<body>
<div class="navbar navbar-inverse" role="navigation">
<div class="container">
<div class="navbar-inner">
<button class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse">
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" style="line-height:15px; height:10px; padding: 10px 10px;" href="/"><img src="/generated/img/logo.svg" title="Back to home."></a>
</div>
<div class="navbar-collapse collapse">
<ul class="nav navbar-nav">
<li><a href="/about/" title="More informations about the project, its sponsors and its authors.">About</a>
</li>
{% if user.is_authenticated %}
<li><a href="/projects/" title="All your projects are here.">Projects</a></li>
{% endif %}
{% if project %}
<li><a href="/projects/{{project.id}}">{{project.name}}</a></li>
{% endif %}
{% if corpus %}
<li><a href="/projects/{{project.id}}/corpora/{{corpus.id}}">{{corpus.name}}</a></li>
{% endif %}
</ul>
<ul class="nav pull-right">
<li class="dropdown">
<a href="#" role="button" class="dropdown-toggle" data-toggle="dropdown" title="That is your login"><i class="icon-user"></i> {{ user }}<i class="caret"></i>
</a>
<ul class="dropdown-menu">
<li><a tabindex="-1" href="http://www.iscpif.fr/tiki-index.php?page=gargantext_feedback" title="Send us a message (bug, thanks, congrats...)">Report Feedback</a></li>
<li class="divider"></li>
{% if user.is_authenticated %}
<li><a tabindex="-1" href="/auth/logout" title="Click here to logout especially on public devices">Logout</a></li>
{% else %}
<li><a tabindex="-1" href="/auth/">Login</a></li>
{% endif %}
</ul>
</li>
</ul>
</div>
</div>
</div>
<!-- this is the tweakbar -->
<div id="defaultop" class="navbar navbar-default">
<div class="navbar-collapse collapse navbar-responsive-collapse">
<div id="left" style="margin:0em 2em;">
<ul class="nav navbar-nav">
<!--
<li>
<a>
<select id="aselector" onchange="console.log('salut monde')" class="selectpicker" data-style="btn btn-success btn-sm" data-width="auto">
<option value="Document" selected>Scholars</option>
<option value="NGram">Keywords</option>
</select>
</a>
</li> -->
<li>
<a>
<button type="button" id="changetype" class="btn btn-success btn-sm">Change Type</button>
</a>
</li>
<li>
<a>
<button type="button" id="changelevel" class="btn btn-info btn-sm" disabled>Change Level</button>
</a>
</li>
<li>
<a>
<div style="margin:0em 2em;" id="unranged-value"></div>
<label style="margin:0em 2em;" for="unranged-value">selector size</label>
</a>
</li>
<li>
<a>
<div id="graphid" style="visibility: hidden;">{{graphfile}}</div>
<input type="hidden" id="list_id" value="{{ list_id }}"></input>
<div id="jquerytemplatenb" style="visibility: hidden;">{{user.id}}</div>
</a>
</li>
<!--
<li>
<a>
<button type="button" onclick="partialGraph.stopForceAtlas2();" class="btn btn-sm">wu</button>
</a>
</li>
-->
</ul>
<ul id="category0" class="nav navbar-nav navbar-right">
<li>
<ul style="list-style-type: none; margin:.5em 0em 1em 1em;">
<li><div id="slidercat0nodesweight"></div></li>
<li><div id="slidercat0edgesweight"></div></li>
<li><div id="slidercat0nodessize"></div></li>
</ul>
</li>
</ul>
<ul id="filterslegend" class="nav navbar-nav navbar-right">
<li>
<ul style="list-style-type: none;">
<li>Nodes</li>
<li>Edges</li>
<li>Size</li>
</ul>
</li>
</ul>
<ul id="category1" class="nav navbar-nav navbar-right">
<li>
<ul style="list-style-type: none; margin:0em 1em;">
<li><div id="slidercat1nodesweight"></div></li>
<li><div id="slidercat1edgesweight"></div></li>
<li><div id="slidercat1nodessize"></div></li>
</ul>
</li>
</ul>
<ul class="nav navbar-nav navbar-right">
<li><a>
<input type="checkbox" id="checkboxdiv" onclick="alertCheckBox(this);">Add</input>
</a></li>
</ul>
<ul class="nav navbar-nav navbar-right">
<li><a>
<input id="searchinput" autocomplete="off" class="form-control input-sm col-lg-8" placeholder="Search" type="text">
</a></li>
</ul>
<ul class="nav navbar-nav navbar-right">
<li>
<a>
<img width="17%" title="Compare with other corpus!" onclick="GetUserPortfolio(); $('#corpuses').modal('show');" src="{% static "js/libs/img2/INTER.png" %}"></img>
</a>
</li>
</ul>
<div class="colorgraph_div"></div>
<div class="sizegraph_div"></div>
<!---->
</div>
<!--
<ul class="nav navbar-nav navbar-right">
<li><a href="#">Link</a></li>
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">Dropdown <b class="caret"></b></a>
<ul class="dropdown-menu">
<li><a href="#">Action</a></li>
<li><a href="#">Another action</a></li>
<li><a href="#">Something else here</a></li>
<li class="divider"></li>
<li><a href="#">Separated link</a></li>
</ul>
</li>
</ul>
-->
</div><!-- /.nav-collapse -->
</div><!-- /.navbar -->
<div id="wrapper">
<div id="zonecentre">
<!-- Page content -->
<div id="sigma-example"></div>
<div style="visibility: hidden;" id="sigma-othergraph"></div>
<img id="semLoader" style="position:absolute; top:50%; left:40%; width:80px;" src="{% static "js/libs/img2/loading-bar.gif" %}"></img>
<ul id="ctlzoom">
<!-- <div class="content-header">
<button id="menu-toggle">X</button>
</div> -->
<!--
<li>
<a href="#" id="geomapicon" onclick="$('#geomapmodal').modal('show'); callGeomap();">
<img title="World Map Distribution" width="34px" src="{% static "js/libs/img2/world.png" %}" ></img>
</a>
</li>
-->
<li>
<a href="#" id="snapicon" onclick="saveGraphIMG();" >
<img title="Take a photo!" width="34px" src="{% static "js/libs/img2/camera.png" %}" ></img>
</a>
</li>
<li>
<a href="#" id="saveAs">
<img width="30px" title="Save As..." src="{% static "js/libs/img2/save.png" %}" ></img>
</a>
</li>
<li>
<a href="#" id="zoomPlusButton" title="S'approcher"> </a>
</li>
<li id="zoomSliderzone">
<div id="zoomSlider"></div>
</li>
<li>
<a href="#" id="zoomMinusButton" title="S'éloigner"> </a>
</li>
<li>
<a href="#" id="lensButton"> </a>
</li>
<li>
<a href="#" id="edgesButton"> </a>
</li>
</ul>
</div>
<!-- Sidebar -->
<div id="leftcolumn">
<div id="tips"></div>
<div id="names"></div>
<div id="ngrams_actions"></div>
<br>
<div id="tab-container" class='tab-container' style="display: none;">
<ul class='etabs'>
<li id="taboppos" class='tab'><a href="#tabs1">Opposite-Neighbors</a></li>
<li id="tabneigh" class='tab'><a href="#tabs2">Neighbors</a></li>
</ul>
<div class='panel-container'>
<div id="tabs1">
<div id="opossiteNodes"></div>
</div>
<div id="tabs2">
<div id="sameNodes"></div>
</div>
</div>
</div>
<!-- <div id="topPapers"></div> -->
<div id="tab-container-top" class='tab-container' style="display: none;">
<ul class='etabs'>
<li id="tabmed" class='tab active'><a href="#tabs3">Pubs</a></li>
<li id="tabgps" class='tab'><a href="#tabs3"></a></li>
</ul>
<div class='panel-container'>
<div id="tabs3">
<div id="topPapers"></div>
</div>
<div id="tabs4">
<div id="topProposals"></div>
</div>
</div>
</div>
<div id="information"></div>
</div>
</div>
<div id="savemodal" class="modal fade">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
<h4 class="modal-title">Exporting GEXF file</h4>
</div>
<div class="modal-body form-horizontal">
What do you want to save?:
<div class="form-group">
<label class="col-lg-2 control-label"> </label>
<div class="col-lg-10">
<div class="radio">
<label>
<input type="radio" name="optionsRadios" id="fullgraph" value="option1" checked="true">
Full Graph
</label>
</div>
<div class="radio">
<label>
<input type="radio" name="optionsRadios" id="visgraph" value="option2">
Visible Graph
</label>
</div>
</div>
</div>
</div>
<div class="modal-body form-horizontal">
Which atributes do you want to keep?:
<div class="form-group">
<label class="col-lg-2 control-label"> </label>
<div class="col-lg-10">
<div class="checkbox">
<label>
<input type="checkbox" name="optionsRadios" id="check_size" value="option1">
Size
</label>
</div>
<div class="checkbox">
<label>
<input type="checkbox" name="optionsRadios" id="check_color" value="option2">
Color
</label>
</div>
</div>
</div>
</div>
<div class="modal-footer">
<button id="closesavemodal" type="button" class="btn btn-default" data-dismiss="modal">Close</button>
<button type="button" class="btn btn-primary" onclick="saveGraph();">Save File</button>
</div>
</div>
</div>
</div>
<div id="corpuses" class="modal fade">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
<h3 class="modal-title">Corpus Comparison Tool</h3>
</div>
<div class="modal-body form-horizontal">
<h4>Choose one corpus:</h4>
<div style="color:red;" id="selected_corpus"></div>
<div id="user_portfolio">
</div>
<div class="modal-footer">
<button id="closecorpuses" type="button" class="btn btn-default" data-dismiss="modal">Close</button>
<button id="add_corpus_tab" type="button" class="btn btn-primary" disabled onclick='printCorpuses();'>Add Tab</button>
</div>
</div>
</div>
</div>
<div id="modalloader" class="modal fade">
<div id="loader" class="loader">
<img src="{% static "js/libs/img2/loader.gif" %}" ></img>
</div>
<div id="closeloader" data-dismiss="modal"></div>
</div>
<script src="{% static "js/jquery/jquery.min.js" %}" type="text/javascript"></script>
<script src="{% static "js/libs/jquery/jquery-ui.js" %}" type="text/javascript"></script>
<script src="{% static "js/libs/jquery/jquery.ba-dotimeout.min.js" %}" type="text/javascript"></script>
<script src="{% static "js/libs/jquery/jquery.mousewheel.min.js" %}" type="text/javascript"></script>
<script type="text/javascript" src="{% static "js/libs/freshslider.1.0.js" %}"></script>
<script type="text/javascript" src="{% static "js/libs/readmore.js" %}"></script>
<script type="text/javascript" src="{% static "js/libs/jquery/jquery.easytabs.min.js" %}"></script>
<script src="{% static "js/libs/bootstrap/js/bootstrap.min.js" %}"></script>
<script src="{% static "js/libs/bootstrap/js/bootstrap-modal.js" %}" type="text/javascript"></script>
<script src="{% static "js/libs/bootstrap/js/bootstrap-hover-dropdown.min.js" %}" type="text/javascript"></script>
<script src="{% static "js/tinawebJS/globalUtils.js" %}" type="text/javascript"></script>
<script src="{% static "js/tinawebJS/plugins/jLouvain.js" %}" type="text/javascript"></script>
<script src="{% static "js/tinawebJS/sigma.min.js" %}" type="text/javascript" language="javascript"></script>
<script src="{% static "js/tinawebJS/sigma.forceatlas2.js" %}" type="text/javascript" language="javascript"></script>
<script src="{% static "js/settings_explorerjs.js" %}" type="text/javascript" language="javascript"></script>
<script src="{% static "js/tinawebJS/sigma.parseCustom.js" %}" type="text/javascript" language="javascript"></script>
<script src="{% static "js/extras_explorerjs.js" %}" type="text/javascript" language="javascript"></script>
<script src="{% static "js/tinawebJS/sigmaUtils.js" %}" type="text/javascript" language="javascript"></script>
<script src="{% static "js/tinawebJS/methods.js" %}" type="text/javascript" language="javascript"></script>
<script src="{% static "js/tinawebJS/minimap.js" %}" type="text/javascript" language="javascript"></script>
<script src="{% static "js/tinawebJS/enviroment.js" %}" type="text/javascript" language="javascript"></script>
<script src="{% static "js/tinawebJS/asyncFA2.js" %}" type="text/javascript" language="javascript"></script>
<script src="{% static "js/tinawebJS/Tinaweb.js" %}" type="text/javascript" language="javascript"></script>
<script src="{% static "js/tinawebJS/main.js" %}" type="text/javascript" language="javascript"></script>
<script type="text/javascript">
function newPopup(url) {
popupWindow = window.open(
url,'popUpWindow','height=700,width=800,left=10,top=10,resizable=yes,scrollbars=yes,toolbar=no,menubar=no,location=no,directories=no,status=no')
}
$('#tab-container').easytabs({updateHash:false});
// $('#tab-container-top').easytabs({updateHash:false});
</script>
</body>
</html>
......@@ -94,10 +94,10 @@
<!-- FIXME a pop up for advanced mode of graphs --!>
<a type="button" class="btn btn-default
{% if view == "conditional" %}active{%endif%}"
href="/projects/{{project.id}}/corpora/{{ corpus.id }}/graph">Graphs (Conditional)</a>
href="/projects/{{project.id}}/corpora/{{ corpus.id }}/explorer?field1=ngrams&amp;field2=ngrams&amp;distance=conditional&amp;bridgeness=5">Graphs (Conditional)</a>
<a type="button" class="btn btn-default
{% if view == "distributional" %}active{%endif%}"
href="/projects/{{project.id}}/corpora/{{ corpus.id }}/graph">Graphs (Distributional)</a>
href="/projects/{{project.id}}/corpora/{{ corpus.id }}/explorer?field1=ngrams&amp;field2=ngrams&amp;distance=distributional&amp;bridgeness=5">Graphs (Distributional)</a>
<!--
<a type="button" class="btn btn-default
{% if view == "journalTerms" %}active{%endif%}"
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment