Commit 138f137e by shenjunjie

品见项目预警模块上测试环境

parent c047b2e2
package com.zhiwei.brandkbs2.common;
import com.zhiwei.brandkbs2.auth.UserThreadLocal;
import com.zhiwei.brandkbs2.util.Tools;
import java.util.Objects;
......@@ -82,6 +83,19 @@ public class RedisKeyPrefix {
*/
private static final String EVENT_ANALYZE_PROGRESS = "BRANDKBS:EVENT:ANALYZE:PROGRESS:";
/**
* 项目预警相关缓存
*/
public static final String PROJECT_WARN_HOT_TOP = "BRANDKBS:HOT_TOP:";
public static String projectWarnHotTopKeyAll(String projectId, String type) {
return RedisKeyPrefix.generateRedisKey(RedisKeyPrefix.PROJECT_WARN_HOT_TOP, projectId, Tools.concat(type, "*"));
}
public static String projectWarnHotTopKey(String projectId, String type, String name) {
return RedisKeyPrefix.generateRedisKey(RedisKeyPrefix.PROJECT_WARN_HOT_TOP, projectId, Tools.concat(type, name));
}
public static String eventAnalysisProgress(String eventId, String projectId) {
return RedisKeyPrefix.generateRedisKey(RedisKeyPrefix.EVENT_ANALYZE_PROGRESS, projectId, eventId);
}
......
package com.zhiwei.brandkbs2.pojo.external;
import com.alibaba.fastjson.JSONObject;
import lombok.AllArgsConstructor;
import lombok.Data;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.List;
/**
......@@ -51,6 +54,17 @@ public class BrandkbsHotTopWarn {
* 上榜时间
*/
private Long time;
public static HotTop createFromJSON(JSONObject json, String topReason, String topName) {
HotTop hotTop = new HotTop();
hotTop.setTitle(json.getString("name"));
hotTop.setTopReason(topReason);
hotTop.setRank(json.getString("lastRank") + "名");
hotTop.setHot(BigDecimal.valueOf(json.getLongValue("lastCount") / 10000.00).setScale(2, RoundingMode.UP) + "w");
hotTop.setTopName(topName);
hotTop.setTime(json.getLong("endTime"));
return hotTop;
}
}
}
......@@ -4,6 +4,7 @@ import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.zhiwei.brandkbs2.common.GenericAttribute;
import com.zhiwei.brandkbs2.common.RedisKeyPrefix;
import com.zhiwei.brandkbs2.config.Constant;
import com.zhiwei.brandkbs2.dao.ChannelDao;
import com.zhiwei.brandkbs2.dao.EventDao;
......@@ -15,8 +16,10 @@ import com.zhiwei.brandkbs2.pojo.*;
import com.zhiwei.brandkbs2.pojo.external.*;
import com.zhiwei.brandkbs2.service.ProjectService;
import com.zhiwei.brandkbs2.service.ProjectWarnService;
import com.zhiwei.brandkbs2.util.RedisUtil;
import com.zhiwei.brandkbs2.util.Tools;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.elasticsearch.index.query.BoolQueryBuilder;
......@@ -29,8 +32,8 @@ import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import javax.annotation.Resource;
import java.io.IOException;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
/**
......@@ -48,6 +51,9 @@ public class ProjectWarnServiceImpl implements ProjectWarnService {
@Value("${ef.external.filterNew.url}")
private String getExternalFilterNewUrl;
@Value("${hot.search.url}")
private String hotSearchUrl;
@Autowired
private RestTemplate restTemplate;
......@@ -63,6 +69,9 @@ public class ProjectWarnServiceImpl implements ProjectWarnService {
@Resource(name = "eventDao")
EventDao eventDao;
@Resource(name = "redisUtil")
RedisUtil redisUtil;
@Override
public ResponseResult getProjectWarnCriteria(String projectId, String type) {
Map<String, Object> res = new HashMap<>();
......@@ -77,12 +86,10 @@ public class ProjectWarnServiceImpl implements ProjectWarnService {
CriteriaHelper.warnCriteriaAdd(res, "contends", nameList, idList);
CriteriaHelper.warnCriteriaAdd(res, "keyword", null);
CriteriaHelper.warnCriteriaAdd(res, "duplicate", Arrays.asList("去重", "不去重"), Arrays.asList(true, false), Collections.singletonList("不去重"));
CriteriaHelper.warnCriteriaAdd(res, "pushInterval", Arrays.asList("10min", "30min", "60min"), Arrays.asList(10 * ONE_MINUTE, 30 * ONE_MINUTE,
60 * ONE_MINUTE), Collections.singletonList("10min"));
CriteriaHelper.warnCriteriaAdd(res, "pushInterval", Arrays.asList("10min", "30min", "60min"), Arrays.asList(10 * ONE_MINUTE, 30 * ONE_MINUTE, 60 * ONE_MINUTE), Collections.singletonList("10min"));
break;
case "渠道参与":
CriteriaHelper.warnCriteriaAdd(res, "friendlyChannel", Arrays.asList("友好渠道", "敏感渠道", "自定义渠道"), Arrays.asList(true, false, null),
Collections.singletonList("自定义渠道"));
CriteriaHelper.warnCriteriaAdd(res, "friendlyChannel", Arrays.asList("友好渠道", "敏感渠道", "自定义渠道"), Arrays.asList(true, false, null), Collections.singletonList("自定义渠道"));
List<Map<String, Object>> thresholdValue = new ArrayList<>();
for (String name : Arrays.asList("友好渠道 友好指数", "敏感渠道 敏感指数")) {
Map<String, Object> map = new HashMap<>();
......@@ -95,8 +102,7 @@ public class ProjectWarnServiceImpl implements ProjectWarnService {
CriteriaHelper.warnCriteriaAdd(res, "primary", Arrays.asList("原创", "转发"), Arrays.asList(false, true), Collections.singletonList("原创"));
CriteriaHelper.warnCriteriaAddSimple(res, "markerTags", Arrays.asList("正面", "中性", "负面"), Collections.singletonList("负面"));
CriteriaHelper.warnCriteriaAdd(res, "duplicate", Arrays.asList("去重", "不去重"), Arrays.asList(true, false), Collections.singletonList("去重"));
CriteriaHelper.warnCriteriaAdd(res, "pushInterval", Arrays.asList("10min", "30min", "60min"), Arrays.asList(10 * ONE_MINUTE, 30 * ONE_MINUTE,
60 * ONE_MINUTE), Collections.singletonList("10min"));
CriteriaHelper.warnCriteriaAdd(res, "pushInterval", Arrays.asList("10min", "30min", "60min"), Arrays.asList(10 * ONE_MINUTE, 30 * ONE_MINUTE, 60 * ONE_MINUTE), Collections.singletonList("10min"));
break;
case "热点事件":
CriteriaHelper.warnCriteriaAddSimple(res, "ownEvent", Arrays.asList("正面", "中性", "负面"), Arrays.asList("正面", "中性", "负面"));
......@@ -107,14 +113,12 @@ public class ProjectWarnServiceImpl implements ProjectWarnService {
List<String> zhiWeiEventList = Arrays.asList("社会", "互联网", "政务", "财经", "企业", "娱乐", "灾难", "违法犯罪", "国际", "体育", "谣言");
CriteriaHelper.warnCriteriaAddSimple(res, "zhiWeiEvent", zhiWeiEventList, zhiWeiEventList);
CriteriaHelper.warnCriteriaAdd(res, "eventTop", Arrays.asList("TOP5", "TOP10"), Arrays.asList(5, 10), Collections.singletonList("TOP10"));
CriteriaHelper.warnCriteriaAdd(res, "timeCycles", Arrays.asList("周一", "周二", "周三", "周四", "周五", "周六", "周日"),
Arrays.asList(2, 3, 4, 5, 6, 7, 1), Collections.singletonList("周一"));
CriteriaHelper.warnCriteriaAdd(res, "timeCycles", Arrays.asList("周一", "周二", "周三", "周四", "周五", "周六", "周日"), Arrays.asList(2, 3, 4, 5, 6, 7, 1), Collections.singletonList("周一"));
break;
case "上榜热搜":
CriteriaHelper.warnCriteriaAddSimple(res, "warnKeyword", null);
CriteriaHelper.warnCriteriaAddSimple(res, "filterKeyword", null);
CriteriaHelper.warnCriteriaAddSimple(res, "listType", Arrays.asList("微博热搜", "微博话题", "微博预热", "头条热搜", "抖音热搜", "知乎热搜", "B站热搜", "快手热榜"),
Collections.singletonList("微博热搜"));
CriteriaHelper.warnCriteriaAddSimple(res, "listType", Arrays.asList("微博热搜", "微博话题", "微博预热", "头条热搜", "抖音热搜", "知乎热搜", "B站热搜", "快手热榜"), Collections.singletonList("微博热搜"));
List<Map<String, Object>> topNewHeight = new ArrayList<>();
for (int i : Arrays.asList(3, 5, 10, 20, 50)) {
Map<String, Object> map = new HashMap<>();
......@@ -125,8 +129,7 @@ public class ProjectWarnServiceImpl implements ProjectWarnService {
res.put("topNewHeight", topNewHeight);
List<String> warnTypeList = Arrays.asList("首次上榜", "排名新高", "掉榜和再次上榜", "导语(仅支持微博平台)");
CriteriaHelper.warnCriteriaAdd(res, "warnType", warnTypeList, Arrays.asList("firstTop", "newHeight", "topOnceAgain", "introduction"), warnTypeList);
CriteriaHelper.warnCriteriaAdd(res, "pushInterval", Collections.singletonList("5min"), Collections.singletonList(5 * ONE_MINUTE),
Collections.singletonList("5min"));
CriteriaHelper.warnCriteriaAdd(res, "pushInterval", Collections.singletonList("5min"), Collections.singletonList(5 * ONE_MINUTE), Collections.singletonList("5min"));
break;
case "案例上新":
return ResponseResult.success();
......@@ -314,8 +317,7 @@ public class ProjectWarnServiceImpl implements ProjectWarnService {
}
// 渠道指数筛选
if (null != config.getFriendlyChannel()) {
Map<String, BaseMap> collect = datas.stream().collect(Collectors.toMap(baseMap ->
Tools.getFid(projectId, "0", baseMap.getPlatform(), baseMap.getRealSource(), baseMap.getSource()), map -> map));
Map<String, BaseMap> collect = datas.stream().collect(Collectors.toMap(baseMap -> Tools.getFid(projectId, "0", baseMap.getPlatform(), baseMap.getRealSource(), baseMap.getSource()), map -> map));
// 通过筛选的fid列表
Map<String, Channel> fidChannel = channelDao.queryUniqueAsync(collect.keySet()).entrySet().stream().map(entry -> {
Channel channel = entry.getValue();
......@@ -434,8 +436,7 @@ public class ProjectWarnServiceImpl implements ProjectWarnService {
return ResponseResult.failure("获取热点事件预警失败");
}
private BrandkbsWarnTemplate brandkbsWarnTemplate4HotEvent(BrandkbsHotEventConfig config, Map<String, List<Event>> eventMap, long start, long end,
AbstractProject project) {
private BrandkbsWarnTemplate brandkbsWarnTemplate4HotEvent(BrandkbsHotEventConfig config, Map<String, List<Event>> eventMap, long start, long end, AbstractProject project) {
int firstCount = eventMap.values().stream().mapToInt(List::size).sum();
if (firstCount == 0) {
return null;
......@@ -467,6 +468,154 @@ public class ProjectWarnServiceImpl implements ProjectWarnService {
return new BrandkbsWarnTemplate(firstCount, key1, key2, key3, key4, new BrandkbsHotEventWarn(collect));
}
private ResponseResult hotTopWaring(String projectId, BrandkbsHotTopConfig config) {
Project project = projectService.getProjectById(projectId);
long end = System.currentTimeMillis();
long start = end - config.getPushInterval();
try {
log.info("获取上榜热搜预警开始,project:{}", project.getProjectName());
// 根据配置榜单类型
List<String> listType = config.getListType();
if (listType.contains("微博热搜")) {
//获取当前微博热搜数据
List<JSONObject> wbRankList = getRankListByType("weibo");
getHotTopList(config, projectId, wbRankList, "微博热搜");
}
log.info("获取上榜热搜预警结束,project:{}", project.getProjectName());
return ResponseResult.success();
} catch (Exception e) {
log.info("获取上榜热搜预警失败,project:{}", project.getProjectName(), e);
}
return ResponseResult.failure("获取上榜热搜预警失败");
}
private List<JSONObject> getRankListByType(String type) {
try {
ResponseEntity<String> response = restTemplate.getForEntity(hotSearchUrl, String.class, type);
return JSON.parseObject(response.getBody()).getJSONArray("data").toJavaList(JSONObject.class);
} catch (Exception e) {
log.error("{} 热搜获取失败:", type, e);
}
return Collections.emptyList();
}
/**
* 处理热搜预警
*
* @param rankList 匹配的热搜结果集
* @param type 类型
*/
private List<BrandkbsHotTopWarn.HotTop> getHotTopList(BrandkbsHotTopConfig config, String projectId, List<JSONObject> rankList, String type) {
if (CollectionUtils.isEmpty(rankList)) {
return Collections.emptyList();
}
// 关键词和过滤词筛选
List<JSONObject> resList = rankList.stream().filter(wbRank -> {
String hotSearchName = wbRank.getString("name").toLowerCase();
return Tools.isContains(config.getWarnKeyword(), hotSearchName) && Tools.isExclusive(config.getFilterKeyword(), hotSearchName);
}).collect(Collectors.toList());
//首次进榜
List<JSONObject> firstList = new ArrayList<>();
//排名新高
List<JSONObject> newTopList = new ArrayList<>();
//掉榜
List<JSONObject> dropList = new ArrayList<>();
//再次上榜
List<JSONObject> reappearList = new ArrayList<>();
//当前上榜的名称
List<String> lastNameList = resList.stream().map(json -> json.getString("name")).collect(Collectors.toList());
//之前掉榜的热搜名称列表
List<String> preDropNameList = new ArrayList<>();
//之前上榜的数据
String projectWarnRedisKey = RedisKeyPrefix.projectWarnHotTopKeyAll(projectId, type + "_*");
Set<String> keys = redisUtil.keys(projectWarnRedisKey);
if (CollectionUtils.isNotEmpty(keys)) {
//查询之前未掉榜的数据
List<JSONObject> preNotDropList = keys.stream().map(key -> JSON.parseObject(redisUtil.get(key))).filter(jsonObject -> Integer.parseInt(jsonObject.getString("lastRank")) > 0).collect(Collectors.toList());
preNotDropList.forEach(pre -> {
String preName = pre.getString("name");
//之前上榜数据本次未出现
if (!lastNameList.contains(preName)) {
//修改状态
String redisKey = RedisKeyPrefix.projectWarnHotTopKey(projectId, type, preName);
JSONObject jsonObject = new JSONObject();
jsonObject.put("name", pre.getString("name"));
jsonObject.put("lastRank", -1);
jsonObject.put("lastCount", -1);
jsonObject.put("url", pre.getString("url"));
jsonObject.put("endTime", pre.getLong("endTime"));
redisUtil.setExpire(redisKey, jsonObject.toJSONString(), 7, TimeUnit.DAYS);
//加入掉榜列表
dropList.add(jsonObject);
}
});
//查询之前掉榜的热搜名称列表
preDropNameList = keys.stream().map(key -> JSON.parseObject(redisUtil.get(key))).filter(jsonObject -> Integer.parseInt(jsonObject.getString("lastRank")) == 1).map(jsonObject -> jsonObject.getString("name")).collect(Collectors.toList());
}
List<String> finalPreDropNameList = preDropNameList;
resList.forEach(json -> {
//热搜名称
String name = json.getString("name");
//当前排名
int lastRank = json.getIntValue("lastRank");
//热度值
long lastCount = json.getLongValue("lastCount");
//链接
String url = json.getString("url");
//时间
Long endTime = json.getLong("endTime");
//获取之前的排名
String hotSearchRedis = RedisKeyPrefix.projectWarnHotTopKey(projectId, type, name);
String result = redisUtil.get(hotSearchRedis);
String oldRank = "";
if (StringUtils.isNotEmpty(result)) {
oldRank = JSON.parseObject(result).getString("lastRank");
}
//更新数据
JSONObject jsonObject = new JSONObject();
jsonObject.put("name", name);
jsonObject.put("lastRank", lastRank);
jsonObject.put("lastCount", lastCount);
jsonObject.put("url", url);
jsonObject.put("endTime", endTime);
redisUtil.setExpire(hotSearchRedis, jsonObject.toJSONString(), 7, TimeUnit.DAYS);
if (finalPreDropNameList.contains(name)) {
//加入再次上榜列表
reappearList.add(jsonObject);
}
//首次上榜
if (StringUtils.isEmpty(oldRank)) {
firstList.add(json);
} else if (Integer.parseInt(oldRank) > 0 && lastRank <= config.getTopNewHeight() && lastRank < Integer.parseInt(oldRank)) { //排名新高
newTopList.add(json);
}
});
List<BrandkbsHotTopWarn.HotTop> hotTops = new ArrayList<>();
if (Boolean.TRUE.equals(config.getFirstTop())) {
hotTops.addAll(firstList.stream().map(json -> BrandkbsHotTopWarn.HotTop.createFromJSON(json, "首次上榜", type)).collect(Collectors.toList()));
}
if (Boolean.TRUE.equals(config.getNewHeight())) {
hotTops.addAll(newTopList.stream().map(json -> BrandkbsHotTopWarn.HotTop.createFromJSON(json, "排名新高", type)).collect(Collectors.toList()));
}
if (Boolean.TRUE.equals(config.getTopOnceAgain())) {
hotTops.addAll(dropList.stream().map(json -> BrandkbsHotTopWarn.HotTop.createFromJSON(json, "掉榜", type)).collect(Collectors.toList()));
hotTops.addAll(reappearList.stream().map(json -> BrandkbsHotTopWarn.HotTop.createFromJSON(json, "再次上榜", type)).collect(Collectors.toList()));
}
if (Boolean.TRUE.equals(config.getIntroduction())) {
List<JSONObject> topLeads = rankList.stream().filter(wbRank -> {
if (wbRank.getString("topicLead") == null) {
return false;
}
String topicLead = wbRank.getString("topicLead").toLowerCase();
return Tools.isContains(config.getWarnKeyword(), topicLead) && Tools.isExclusive(config.getFilterKeyword(), topicLead);
}).collect(Collectors.toList());
hotTops.addAll(topLeads.stream().map(json -> BrandkbsHotTopWarn.HotTop.createFromJSON(json, "导语", type)).collect(Collectors.toList()));
}
return hotTops;
}
private List<BaseMap> duplicateBaseMapList(List<BaseMap> datas) {
Map<String, BaseMap> duplicateMap = new LinkedHashMap<>();
for (BaseMap data : datas) {
......@@ -580,32 +729,38 @@ public class ProjectWarnServiceImpl implements ProjectWarnService {
@Override
public ResponseResult getYuqingWaring(String projectId, BrandkbsYuQingConfig config) {
return ResponseResult.success(getWarnTemplate(new BrandkbsYuQingWarn(Arrays.asList(getBaseMapCompoundTemplate(), getBaseMapCompoundTemplate(),
getBaseMapCompoundTemplate(), getBaseMapCompoundTemplate(), getBaseMapCompoundTemplate()))));
// return ResponseResult.success(getWarnTemplate(new BrandkbsYuQingWarn(Arrays.asList(getBaseMapCompoundTemplate(), getBaseMapCompoundTemplate(),
// getBaseMapCompoundTemplate(), getBaseMapCompoundTemplate(), getBaseMapCompoundTemplate()))));
return yuqingWaringReal(projectId, config);
}
@Override
public ResponseResult getChannelWaring(String projectId, BrandkbsChannelConfig brandkbsChannelConfig) {
return ResponseResult.success(getWarnTemplate(new BrandkbsChannelWarn(Arrays.asList(getBaseMapCompoundTemplate(), getBaseMapCompoundTemplate(),
getBaseMapCompoundTemplate(), getBaseMapCompoundTemplate(), getBaseMapCompoundTemplate()), "敏感渠道(敏感指数>70)")));
public ResponseResult getChannelWaring(String projectId, BrandkbsChannelConfig config) {
// return ResponseResult.success(getWarnTemplate(new BrandkbsChannelWarn(Arrays.asList(getBaseMapCompoundTemplate(), getBaseMapCompoundTemplate(),
// getBaseMapCompoundTemplate(), getBaseMapCompoundTemplate(), getBaseMapCompoundTemplate()), "敏感渠道(敏感指数>70)")));
return channelWarnReal(projectId, config);
}
@Override
public ResponseResult getHotEventWaring(String projectId, BrandkbsHotEventConfig brandkbsHotEventConfig) {
return ResponseResult.success(getWarnTemplate(new BrandkbsHotEventWarn(Arrays.asList(getHotEventTemplate(), getHotEventTemplate(),
getHotEventTemplate(), getHotEventTemplate(), getHotEventTemplate()))));
public ResponseResult getHotEventWaring(String projectId, BrandkbsHotEventConfig config) {
// return ResponseResult.success(getWarnTemplate(new BrandkbsHotEventWarn(Arrays.asList(getHotEventTemplate(), getHotEventTemplate(),
// getHotEventTemplate(), getHotEventTemplate(), getHotEventTemplate()))));
return hotEventWaring(projectId, config);
}
@Override
public ResponseResult getHotTopWaring(String projectId, BrandkbsHotTopConfig config) {
return ResponseResult.success(getWarnTemplate(new BrandkbsHotTopWarn("阿里", "腾讯", Arrays.asList(getHotTopTemplate(), getHotTopTemplate(),
getHotTopTemplate(), getHotTopTemplate(), getHotTopTemplate()))));
// return ResponseResult.success(getWarnTemplate(new BrandkbsHotTopWarn("阿里", "腾讯", Arrays.asList(getHotTopTemplate(), getHotTopTemplate(),
// getHotTopTemplate(), getHotTopTemplate(), getHotTopTemplate()))));
return hotTopWaring(projectId, config);
}
@Override
public ResponseResult getNewCaseWaring(String projectId) {
return ResponseResult.success(getWarnTemplate(new BrandkbsNewCaseWarn(Arrays.asList(getNewCaseWarn(), getNewCaseWarn(), getNewCaseWarn(),
getNewCaseWarn(), getNewCaseWarn()))));
// return ResponseResult.success(getWarnTemplate(new BrandkbsNewCaseWarn(Arrays.asList(getNewCaseWarn(), getNewCaseWarn(), getNewCaseWarn(),
// getNewCaseWarn(), getNewCaseWarn()))));
// TODO
return ResponseResult.success();
}
public static class CriteriaHelper {
......
......@@ -5,6 +5,7 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import java.util.Set;
import java.util.concurrent.TimeUnit;
/**
......@@ -74,6 +75,10 @@ public class RedisUtil {
return stringRedisTemplate.opsForValue().get(key);
}
public Set<String> keys(String key) {
return stringRedisTemplate.keys(key);
}
public void remove(String key) {
setExpire(key, "-1", 1, TimeUnit.SECONDS);
}
......
......@@ -884,6 +884,34 @@ public class Tools {
return false;
}
public static boolean isExclusive(String keyword, String content) {
return isExclusive(Arrays.asList(keyword.split("\\|")), content);
}
/**
* 是否排除关键字 只有或逻辑
*
* @param filterKeywords 排除关键词
* @param data 字符串标题+内容
* @return 是否包含关键字
*/
public static boolean isExclusive(List<String> filterKeywords, String data) {
boolean exclusive = true;
for (String key : filterKeywords) {
if(StringUtils.isBlank(key)){
continue;
}
if (data.contains(key.toLowerCase())) {
exclusive=false;
}
if (!exclusive) {
//已经满足条件,视为有效数据,结束循环
break;
}
}
return exclusive;
}
public static JSONObject getBrandkbsHitMap(Map<String, Object> esMap, String hitKey) {
List<Map<String, Object>> cacheMaps = (List<Map<String, Object>>) esMap.get(GenericAttribute.ES_BRANDKBS_CACHE_MAPS);
for (Map<String, Object> cacheMap : cacheMaps) {
......
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