Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
N
neo4jRelation
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
qiuchaofei
neo4jRelation
Commits
2e5f32ae
Commit
2e5f32ae
authored
Jun 22, 2022
by
qiuchaofei
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
1提供系统总图结构,2从excel导入数据
parent
144dfcf6
Hide whitespace changes
Inline
Side-by-side
Showing
8 changed files
with
427 additions
and
14 deletions
+427
-14
pom.xml
pom.xml
+22
-6
DataRelationAnalyController.java
...adataRelation/controller/DataRelationAnalyController.java
+16
-4
SystemNode.java
.../keymobile/metadata/metadataRelation/pojo/SystemNode.java
+28
-0
SystemRelation.java
...mobile/metadata/metadataRelation/pojo/SystemRelation.java
+46
-0
ReturnEdge.java
...metadata/metadataRelation/pojo/returnBean/ReturnEdge.java
+13
-0
Excel2Neo4jService.java
...metadata/metadataRelation/service/Excel2Neo4jService.java
+7
-0
Excel2Neo4jServiceImpl.java
...metadataRelation/service/impl/Excel2Neo4jServiceImpl.java
+226
-0
MetadataServiceImpl.java
...ta/metadataRelation/service/impl/MetadataServiceImpl.java
+69
-4
No files found.
pom.xml
View file @
2e5f32ae
...
...
@@ -41,6 +41,7 @@
<version>
${auth.version}
</version>
</dependency>
<!-- 添加neo4j的jar包 -->
<dependency>
<groupId>
org.springframework.boot
</groupId>
...
...
@@ -59,13 +60,13 @@
<version>0.3.1</version>
</dependency>
<dependency>
<groupId>org.neo4j</groupId>
<artifactId>neo4j-ogm-bolt-driver</artifactId>
<version>3.0.0</version>
</dependency>
-->
-->
<dependency>
<groupId>
org.neo4j
</groupId>
<artifactId>
neo4j-ogm-bolt-driver
</artifactId>
<version>
3.0.0
</version>
</dependency>
<dependency>
<groupId>
org.neo4j
</groupId>
<artifactId>
neo4j-ogm-embedded-driver
</artifactId>
...
...
@@ -77,6 +78,21 @@
<version>
1.7.5
</version>
</dependency>
<!-- excel文件解析依赖 -->
<!-- https://mvnrepository.com/artifact/org.apache.poi/poi -->
<dependency>
<groupId>
org.apache.poi
</groupId>
<artifactId>
poi
</artifactId>
<version>
4.1.0
</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.poi/poi-ooxml -->
<dependency>
<groupId>
org.apache.poi
</groupId>
<artifactId>
poi-ooxml
</artifactId>
<version>
4.1.0
</version>
</dependency>
<dependency>
<groupId>
io.springfox
</groupId>
<artifactId>
springfox-swagger2
</artifactId>
...
...
src/main/java/com/keymobile/metadata/metadataRelation/controller/DataRelationAnalyController.java
View file @
2e5f32ae
...
...
@@ -2,10 +2,7 @@ package com.keymobile.metadata.metadataRelation.controller;
import
com.keymobile.metadata.metadataRelation.pojo.returnBean.ReturnNode
;
import
com.keymobile.metadata.metadataRelation.pojo.returnBean.ReturnReslult
;
import
com.keymobile.metadata.metadataRelation.service.DataRelationAnalyService
;
import
com.keymobile.metadata.metadataRelation.service.ISchemaService
;
import
com.keymobile.metadata.metadataRelation.service.ISystemService
;
import
com.keymobile.metadata.metadataRelation.service.ITableService
;
import
com.keymobile.metadata.metadataRelation.service.*
;
import
io.swagger.annotations.Api
;
import
io.swagger.annotations.ApiOperation
;
import
org.slf4j.Logger
;
...
...
@@ -31,6 +28,8 @@ public class DataRelationAnalyController {
@Autowired
private
ITableService
tableService
;
@Autowired
private
Excel2Neo4jService
excel2Neo4jService
;
@Autowired
private
ISchemaService
schemaService
;
...
...
@@ -135,4 +134,17 @@ public class DataRelationAnalyController {
logger
.
info
(
"子节点id找出父节点。子节点id:"
+
childId
);
return
tableService
.
getParentByChildId
(
childId
);
}
//解析excel,存入neo4j
@ApiOperation
(
tags
=
""
,
value
=
"从excel生成neo4j的节点与关系。"
)
@RequestMapping
(
path
=
"/generalRelationFromExcel"
,
method
=
RequestMethod
.
GET
)
public
String
generalRelationFromExcel
(
String
filePath
)
{
logger
.
info
(
"从excel生成neo4j的节点与关系:"
+
filePath
);
if
(
filePath
==
null
||
""
.
equals
(
filePath
)
){
filePath
=
"D:\\dev\\neo4j-community-3.3.5\\systemRelation.xlsx"
;
}
return
excel2Neo4jService
.
generalRelationFromExcel
(
filePath
)
;
}
}
src/main/java/com/keymobile/metadata/metadataRelation/pojo/SystemNode.java
0 → 100644
View file @
2e5f32ae
package
com
.
keymobile
.
metadata
.
metadataRelation
.
pojo
;
import
java.util.HashMap
;
import
java.util.Map
;
public
class
SystemNode
{
private
String
name
;
public
String
getName
()
{
return
name
;
}
public
void
setName
(
String
name
)
{
this
.
name
=
name
;
}
public
Map
<
String
,
String
>
getNodeAttributeMap
()
{
return
nodeAttributeMap
;
}
public
void
setNodeAttributeMap
(
Map
<
String
,
String
>
nodeAttributeMap
)
{
this
.
nodeAttributeMap
=
nodeAttributeMap
;
}
private
Map
<
String
,
String
>
nodeAttributeMap
=
new
HashMap
<>();
}
src/main/java/com/keymobile/metadata/metadataRelation/pojo/SystemRelation.java
0 → 100644
View file @
2e5f32ae
package
com
.
keymobile
.
metadata
.
metadataRelation
.
pojo
;
import
java.util.HashMap
;
import
java.util.Map
;
public
class
SystemRelation
{
private
String
id
;
private
String
from
;
private
String
to
;
public
String
getId
()
{
return
id
;
}
public
void
setId
(
String
id
)
{
this
.
id
=
id
;
}
public
String
getFrom
()
{
return
from
;
}
public
void
setFrom
(
String
from
)
{
this
.
from
=
from
;
}
public
String
getTo
()
{
return
to
;
}
public
void
setTo
(
String
to
)
{
this
.
to
=
to
;
}
public
Map
<
String
,
String
>
getAttributeMap
()
{
return
attributeMap
;
}
public
void
setAttributeMap
(
Map
<
String
,
String
>
attributeMap
)
{
this
.
attributeMap
=
attributeMap
;
}
private
Map
<
String
,
String
>
attributeMap
=
new
HashMap
<>();
}
src/main/java/com/keymobile/metadata/metadataRelation/pojo/returnBean/ReturnEdge.java
View file @
2e5f32ae
package
com
.
keymobile
.
metadata
.
metadataRelation
.
pojo
.
returnBean
;
import
java.util.HashMap
;
import
java.util.Map
;
public
class
ReturnEdge
{
private
String
edgeId
;
...
...
@@ -9,6 +12,16 @@ public class ReturnEdge {
private
String
type
;
public
Map
<
String
,
String
>
getAttributeMaps
()
{
return
attributeMaps
;
}
public
void
setAttributeMaps
(
Map
<
String
,
String
>
attributeMaps
)
{
this
.
attributeMaps
=
attributeMaps
;
}
private
Map
<
String
,
String
>
attributeMaps
=
new
HashMap
<>();
public
String
getEdgeId
()
{
return
edgeId
;
}
...
...
src/main/java/com/keymobile/metadata/metadataRelation/service/Excel2Neo4jService.java
0 → 100644
View file @
2e5f32ae
package
com
.
keymobile
.
metadata
.
metadataRelation
.
service
;
public
interface
Excel2Neo4jService
{
String
generalRelationFromExcel
(
String
filePath
);
}
src/main/java/com/keymobile/metadata/metadataRelation/service/impl/Excel2Neo4jServiceImpl.java
0 → 100644
View file @
2e5f32ae
package
com
.
keymobile
.
metadata
.
metadataRelation
.
service
.
impl
;
import
com.keymobile.metadata.metadataRelation.pojo.SystemNode
;
import
com.keymobile.metadata.metadataRelation.pojo.SystemRelation
;
import
com.keymobile.metadata.metadataRelation.service.Excel2Neo4jService
;
import
org.apache.poi.hssf.usermodel.HSSFWorkbook
;
import
org.apache.poi.ss.usermodel.Cell
;
import
org.apache.poi.ss.usermodel.Row
;
import
org.apache.poi.ss.usermodel.Sheet
;
import
org.apache.poi.ss.usermodel.Workbook
;
import
org.apache.poi.xssf.usermodel.XSSFWorkbook
;
import
org.neo4j.driver.v1.Session
;
import
org.neo4j.driver.v1.StatementResult
;
import
org.springframework.beans.factory.annotation.Autowired
;
import
org.springframework.stereotype.Service
;
import
java.io.File
;
import
java.io.FileInputStream
;
import
java.io.FileNotFoundException
;
import
java.io.IOException
;
import
java.util.ArrayList
;
import
java.util.HashMap
;
import
java.util.List
;
import
java.util.Map
;
@Service
public
class
Excel2Neo4jServiceImpl
implements
Excel2Neo4jService
{
@Autowired
private
Session
neo4jSession
;
@Override
public
String
generalRelationFromExcel
(
String
filePath
)
{
File
file
=
new
File
(
filePath
);
//先删除数据
String
deleteCypher
=
"MATCH (n:softplatform) detach delete n"
;
StatementResult
run
=
neo4jSession
.
run
(
deleteCypher
);
List
<
SystemNode
>
systemNodeList
=
new
ArrayList
<>();
List
<
SystemRelation
>
systemRelationList
=
new
ArrayList
<>();
FileInputStream
is
=
null
;
Workbook
workbook
=
null
;
try
{
String
fileName
=
file
.
getName
();
// 创建Workbook工作薄对象,表示整个excel
// 获取excel文件的io流
is
=
new
FileInputStream
(
file
);
// 根据文件后缀名不同(xls和xlsx)获得不同的Workbook实现类对象
if
(
fileName
.
endsWith
(
".xlsx"
))
{
// 2007 及2007以上
workbook
=
new
XSSFWorkbook
(
is
);
}
// 第一个sheet是 node,先解析入neo4j。
// 第二个sheet 是 关系,
if
(
workbook
!=
null
)
{
// 开始循环sheet页 从0开始(workbook.getNumberOfSheets()获取sheet页的数量)
for
(
int
sheetIndex
=
0
;
sheetIndex
<
workbook
.
getNumberOfSheets
();
sheetIndex
++)
{
Sheet
sheet
=
workbook
.
getSheetAt
(
sheetIndex
);
if
(
sheet
==
null
)
{
continue
;
}
if
(
"系统节点"
.
equals
(
sheet
.
getSheetName
())){
// 获得当前sheet的开始行
int
firstRowNum
=
sheet
.
getFirstRowNum
();
// 获得当前sheet的结束行
int
lastRowNum
=
sheet
.
getLastRowNum
();
// 循环除第一行外的所有行
for
(
int
rowNum
=
firstRowNum
+
1
;
rowNum
<=
lastRowNum
;
rowNum
++)
{
// 获得当前行
Row
row
=
sheet
.
getRow
(
rowNum
);
if
(
row
==
null
)
{
continue
;
}
// 获得当前行的开始列
int
firstCellNum
=
row
.
getFirstCellNum
();
// 获得当前行的列数
int
lastCellNum
=
row
.
getLastCellNum
();
String
[]
cells
=
new
String
[
row
.
getLastCellNum
()];
// 循环当前行
Map
<
String
,
String
>
nodeAttribute
=
new
HashMap
<>();
String
systemName
=
""
;
SystemNode
systemNode
=
new
SystemNode
();
for
(
int
cellNum
=
firstCellNum
;
cellNum
<
lastCellNum
;
cellNum
++)
{
Cell
cell
=
row
.
getCell
(
cellNum
);
String
value
=
getCellValue
(
cell
);
if
(
cellNum
==
1
){
systemName
=
getCellValue
(
cell
);
systemNode
.
setName
(
systemName
);
}
else
if
(
cellNum
==
2
){
nodeAttribute
.
put
(
"内部"
,
getCellValue
(
cell
));
systemNode
.
getNodeAttributeMap
().
put
(
"内外部系统"
,
getCellValue
(
cell
));
}
else
if
(
cellNum
==
3
){
systemNode
.
getNodeAttributeMap
().
put
(
"行"
,
getCellValue
(
cell
));
}
else
if
(
cellNum
==
4
){
systemNode
.
getNodeAttributeMap
().
put
(
"列"
,
getCellValue
(
cell
));;
}
}
systemNodeList
.
add
(
systemNode
);
}
}
else
if
(
"关系"
.
equals
(
sheet
.
getSheetName
())){
// 获得当前sheet的开始行
int
firstRowNum
=
sheet
.
getFirstRowNum
();
// 获得当前sheet的结束行
int
lastRowNum
=
sheet
.
getLastRowNum
();
// 循环除第一行外的所有行
for
(
int
rowNum
=
firstRowNum
+
1
;
rowNum
<=
lastRowNum
;
rowNum
++)
{
// 获得当前行
Row
row
=
sheet
.
getRow
(
rowNum
);
if
(
row
==
null
)
{
continue
;
}
// 获得当前行的开始列
int
firstCellNum
=
row
.
getFirstCellNum
();
// 获得当前行的列数
int
lastCellNum
=
row
.
getLastCellNum
();
String
[]
cells
=
new
String
[
row
.
getLastCellNum
()];
// 循环当前行
SystemRelation
systemRelation
=
new
SystemRelation
();
String
fromId
=
getCellValue
(
row
.
getCell
(
1
));
String
toId
=
getCellValue
(
row
.
getCell
(
2
));
systemRelation
.
setFrom
(
fromId
);
systemRelation
.
setTo
(
toId
);
systemRelation
.
getAttributeMap
().
put
(
"形式"
,
getCellValue
(
row
.
getCell
(
3
)));
systemRelation
.
getAttributeMap
().
put
(
"协议"
,
getCellValue
(
row
.
getCell
(
4
)));
systemRelation
.
getAttributeMap
().
put
(
"业务类别"
,
getCellValue
(
row
.
getCell
(
5
)));
systemRelation
.
getAttributeMap
().
put
(
"数据结构"
,
getCellValue
(
row
.
getCell
(
6
)));
systemRelation
.
getAttributeMap
().
put
(
"业务说明"
,
getCellValue
(
row
.
getCell
(
7
)));
systemRelationList
.
add
(
systemRelation
);
}
}
}
}
}
catch
(
Exception
e
){
e
.
printStackTrace
();
}
finally
{
try
{
is
.
close
();
}
catch
(
IOException
e
)
{
System
.
out
.
println
(
e
.
toString
());
}
}
for
(
SystemNode
systemNode:
systemNodeList
){
String
hang
=
systemNode
.
getNodeAttributeMap
().
get
(
"行"
);
String
lie
=
systemNode
.
getNodeAttributeMap
().
get
(
"列"
);
if
(
hang
==
null
||
""
.
equals
(
hang
.
trim
())){
hang
=
"0"
;
}
if
(
lie
==
null
||
""
.
equals
(
lie
.
trim
())){
lie
=
"0"
;
}
String
addNodeCypher
=
" create (n:softplatform {name:\""
+
systemNode
.
getName
()+
"\", "
+
"内外部系统:\""
+
systemNode
.
getNodeAttributeMap
().
get
(
"内外部系统"
)+
"\" ,行:"
+
hang
+
" ,列:"
+
lie
+
"}) return n"
;
neo4jSession
.
run
(
addNodeCypher
);
}
for
(
SystemRelation
relation
:
systemRelationList
){
String
fromId
=
relation
.
getFrom
();
String
toId
=
relation
.
getTo
();
String
type
=
relation
.
getAttributeMap
().
get
(
"形式"
);
String
xieyi
=
relation
.
getAttributeMap
().
get
(
"协议"
);
String
zubie
=
relation
.
getAttributeMap
().
get
(
"业务类别"
);
String
shujujiegou
=
relation
.
getAttributeMap
().
get
(
"数据结构"
);
String
yewushuoming
=
relation
.
getAttributeMap
().
get
(
"业务说明"
);
String
addRelationCypher
=
" match( a:softplatform {name:\""
+
fromId
+
"\"} ),(b:softplatform {name:\""
+
toId
+
"\"} ) "
+
" merge (a)-[r:"
+
type
+
"]-> (b) "
+
"set r.协议=\""
+
xieyi
+
"\" ,r.业务类别 = \""
+
zubie
+
"\" ,r.数据结构= \""
+
shujujiegou
+
"\",r.业务说明= \""
+
yewushuoming
+
"\" "
+
" return r "
;
neo4jSession
.
run
(
addRelationCypher
);
}
return
""
;
}
public
static
String
getCellValue
(
Cell
cell
)
{
String
cellValue
=
""
;
if
(
cell
==
null
)
{
return
cellValue
;
}
// 判断数据的类型
switch
(
cell
.
getCellType
())
{
case
NUMERIC:
// 数字
cellValue
=
String
.
valueOf
(
cell
.
getNumericCellValue
());
if
(
cellValue
.
indexOf
(
"."
)
>
0
)
{
cellValue
=
cellValue
.
replaceAll
(
"0+?$"
,
""
);
// 去掉多余的0
cellValue
=
cellValue
.
replaceAll
(
"[.]$"
,
""
);
// 如最后一位是.则去掉
}
break
;
case
STRING:
// 字符串
cellValue
=
String
.
valueOf
(
cell
.
getStringCellValue
());
break
;
case
BOOLEAN:
// Boolean
cellValue
=
String
.
valueOf
(
cell
.
getBooleanCellValue
());
break
;
case
FORMULA:
// 公式
cellValue
=
String
.
valueOf
(
cell
.
getCellFormula
());
break
;
case
BLANK:
// 空值
cellValue
=
""
;
break
;
case
ERROR:
// 故障
cellValue
=
"非法字符"
;
break
;
default
:
cellValue
=
"未知类型"
;
break
;
}
return
cellValue
;
}
}
src/main/java/com/keymobile/metadata/metadataRelation/service/impl/MetadataServiceImpl.java
View file @
2e5f32ae
...
...
@@ -535,7 +535,6 @@ public class MetadataServiceImpl implements IMetadataService {
ReturnNode
returnNode
=
new
ReturnNode
();
returnNode
.
setId
(
neo4jSystem
.
getMetadataId
());
returnNode
.
setName
(
neo4jSystem
.
getName
());
returnNode
.
setIcon
(
modelName
);
if
(!
metaModelMap
.
containsKey
(
modelName
))
{
MetaModel
model
=
new
MetaModel
();
...
...
@@ -560,7 +559,6 @@ public class MetadataServiceImpl implements IMetadataService {
String
cypher
=
"match data=(na:softplatform)<-[r]->(nb:softplatform) return data "
;
StatementResult
statementResult
=
session
.
run
(
cypher
);
String
modelName
=
SystemModelName
;
Map
<
Long
,
ReturnNode
>
nodesMap
=
new
HashMap
<>();
Map
<
String
,
MetaModel
>
metaModelMap
=
new
HashMap
<>();
int
size
=
300
;
...
...
@@ -571,10 +569,10 @@ public class MetadataServiceImpl implements IMetadataService {
if
(
value
.
type
().
name
().
equals
(
"PATH"
))
{
Path
p
=
value
.
asPath
();
Iterable
<
Node
>
nodes
=
p
.
nodes
();
getNodesMapFromNeo4j
(
size
,
modelName
,
nodesMap
,
metaModelMap
,
nodes
);
getNodesMapFromNeo4j
System
(
nodesMap
,
metaModelMap
,
nodes
);
Iterable
<
Relationship
>
relationships
=
p
.
relationships
();
getRelationFromneo4jRelation
(
returnReslult
,
nodesMap
,
relationships
);
getRelationFromneo4jRelation
System
(
returnReslult
,
nodesMap
,
relationships
);
}
}
}
...
...
@@ -587,6 +585,73 @@ public class MetadataServiceImpl implements IMetadataService {
return
returnReslult
;
}
private
void
getRelationFromneo4jRelationSystem
(
ReturnReslult
returnReslult
,
Map
<
Long
,
ReturnNode
>
nodesMap
,
Iterable
<
Relationship
>
relationships
)
{
for
(
Relationship
relationship
:
relationships
)
{
Long
startID
=
relationship
.
startNodeId
();
Long
endID
=
relationship
.
endNodeId
();
String
rType
=
relationship
.
type
();
/**
* asMap 相当于 节点的properties属性信息
*/
// relationship.id();
//
String
xieyi
=
""
+
relationship
.
asMap
().
get
(
"协议"
);
String
yewuleibie
=
""
+
relationship
.
asMap
().
get
(
"业务类别"
);
String
shujujiegou
=
""
+
relationship
.
asMap
().
get
(
"数据结构"
);
String
yewushuoming
=
""
+
relationship
.
asMap
().
get
(
"业务说明"
);
String
startMetaId
=
""
;
String
endMEtaId
=
""
;
if
(
nodesMap
.
containsKey
(
startID
))
{
startMetaId
=
nodesMap
.
get
(
startID
).
getId
();
}
if
(
nodesMap
.
containsKey
(
endID
))
{
endMEtaId
=
nodesMap
.
get
(
endID
).
getId
();
}
if
(!
StringUtils
.
isBlank
(
startMetaId
)
&&
!
StringUtils
.
isBlank
(
endMEtaId
)){
ReturnEdge
edge
=
new
ReturnEdge
();
edge
.
setEdgeId
(
String
.
valueOf
(
relationship
.
id
()));
edge
.
setFromId
(
startMetaId
);
edge
.
setToId
(
endMEtaId
);
edge
.
setType
(
rType
);
edge
.
getAttributeMaps
().
put
(
"协议"
,
xieyi
);
edge
.
getAttributeMaps
().
put
(
"业务说明"
,
yewushuoming
);
edge
.
getAttributeMaps
().
put
(
"数据结构"
,
shujujiegou
);
edge
.
getAttributeMaps
().
put
(
"业务说明"
,
yewushuoming
);
returnReslult
.
getEdges
().
add
(
edge
);
}
}
}
private
void
getNodesMapFromNeo4jSystem
(
Map
<
Long
,
ReturnNode
>
nodesMap
,
Map
<
String
,
MetaModel
>
metaModelMap
,
Iterable
<
Node
>
nodes
)
{
for
(
Node
node
:
nodes
)
{
Map
<
String
,
Object
>
stringObjectMap
=
node
.
asMap
();
Neo4jSystem
neo4jSystem
=
new
Neo4jSystem
();
Neo4jTool
.
transMap2Bean
(
stringObjectMap
,
neo4jSystem
);
// metaDataList.add(data);
if
(
neo4jSystem
.
getMetadataId
()
==
null
){
neo4jSystem
.
setMetadataId
(
SystemModelEqual
+
neo4jSystem
.
getName
());
}
ReturnNode
returnNode
=
new
ReturnNode
();
returnNode
.
setId
(
neo4jSystem
.
getMetadataId
());
returnNode
.
setName
(
neo4jSystem
.
getName
());
returnNode
.
getAttributeMaps
().
put
(
"内外部系统"
,
""
+
stringObjectMap
.
get
(
"内外部系统"
));
returnNode
.
getAttributeMaps
().
put
(
"行"
,
""
+
stringObjectMap
.
get
(
"行"
));
returnNode
.
getAttributeMaps
().
put
(
"列"
,
""
+
stringObjectMap
.
get
(
"列"
));
returnNode
.
setType
(
"Neo4jSystem"
);
nodesMap
.
put
(
node
.
id
(),
returnNode
);
}
}
@Override
public
Map
<
String
,
String
>
getAutoMatchByInput
(
String
name
,
int
countInt
,
int
offsetInt
)
{
logger
.
info
(
"开始模糊查找:"
+
name
);
...
...
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