Skip to content
Snippets Groups Projects
Commit 9ad45498 authored by Reinder Kraaij's avatar Reinder Kraaij :eye:
Browse files

Merge branch 'TMSS-2717-Refactor-Details-Summary-+-Ui-Tweaks' into 'master'

Resolve TMSS-2717 "Refactor details summary + ui tweaks"

Closes TMSS-2717

See merge request !1212
parents 47082178 cbb5789c
No related branches found
No related tags found
1 merge request!1212Resolve TMSS-2717 "Refactor details summary + ui tweaks"
Showing
with 108 additions and 91 deletions
......@@ -594,7 +594,7 @@ $vh: 1vh;
}
.legend-sunset {
background-color: #FFB74D;
background-color: #F5D49A;
color: black;
}
......
......@@ -60,7 +60,7 @@ function getTimelineHeaders(headerSettings) {
{({ getRootProps }) => { //getRootProps is mandatory for the library
return <div {...getRootProps()} className="sidebar-header">
<div className="sidebar-header-row">UTC</div>
<div className="sidebar-header-row" title="Approximate LST@CS002" >~ LST</div>
<div className="sidebar-header-row" title="LST@CS002" >LST</div>
</div>
}}
</SidebarHeader>
......@@ -232,7 +232,7 @@ export default function WeekView() {
const currentGroupDate = group.date;
if (isDebugLoggingOn) console.log(" mouseOverItem[" + keys.itemGroupKey + "]=" + mouseOverItem?.[keys.itemGroupKey] + "date:" + currentGroupDate, "isSingleStation:" + group.isSingleStation, groups[currentGroupIndex])
await getCursorDateTimes(cursorTimeUTC, currentGroupIndex, currentGroupDate, headerSettings.lstShiftInSeconds, group.title, group.isSingleStation, setCursorInformation);
await getCursorDateTimes(cursorTimeUTC, currentGroupIndex, currentGroupDate, headerSettings.lstShiftInSeconds, group.designatedStation, group.isSingleStation, setCursorInformation);
let newGroups = groups.map(group => ({ ...group }));
newGroups.forEach(group => delete group.cursorInfo)
newGroups[currentGroupIndex].cursorInfo = cursorInformation
......@@ -294,7 +294,7 @@ export default function WeekView() {
if (summaryItem.type === "RESERVATION") {
fetchReservationSummaryInformation(summaryItem.id, setSummarySettings).catch(e => console.error("Couldn't retrieve reservations details for id: ", summaryItem.id, e))
} else { //a Scheduling Unit summary is selected
fetchSUSummaryInformation(summaryItem.id, setSummarySettings).catch(e => console.error("Couldn't retrieve summary details for id: ", summaryItem.id, e))
fetchSUSummaryInformation(summaryItem.id, setSummarySettings,summaryItem.item).catch(e => console.error("Couldn't retrieve summary details for id: ", summaryItem.id, e))
}
}
}, [summaryItem]);
......@@ -396,6 +396,8 @@ export default function WeekView() {
<Filters timelineStore={timelineStore}
visibleStartTime={visibleTime.start}
endTime={endTime}
startTime={startTime}
setItems={setItems}
data={data}
setRefetchToggle={setRefetchToggle}
......
......@@ -7,14 +7,14 @@ export function expandTimeLineRowsForGrouping(timeLineRows, groupingkey) {
const includeStationsMissingInfo = groupingkey == "station_missing_group"
const includeStationsgroupInfo = groupingkey == "stationgroup_group"
const includeTasksInfo = groupingkey =="project_task_group"
const includeTasksInfo = groupingkey == "project_task_group"
let expandedschedulingUnitItems = [];
if (includeStationsgroupInfo) expandTimeLineForStationGroup(timeLineRows, expandedschedulingUnitItems);
if (includeStationsInfo) expandTimeLineForStations(timeLineRows, expandedschedulingUnitItems, "specified_stations", groupingkey);
if (includeStationsUsedInfo) expandTimeLineForStations(timeLineRows, expandedschedulingUnitItems, "used_stations", groupingkey);
if (includeStationsMissingInfo) expandTimeLineForStations(timeLineRows, expandedschedulingUnitItems, "missing_stations", groupingkey);
if (includeTasksInfo) expandTimeLineForProjectTask(timeLineRows, expandedschedulingUnitItems);
if (includeTasksInfo) expandTimeLineForProjectTask(timeLineRows, expandedschedulingUnitItems);
return expandedschedulingUnitItems;
}
......@@ -56,17 +56,17 @@ function expandTimeLineForProjectTask(timeLineRows, expandedschedulingUnitItems)
let tasks = timelinerow.task_blueprints;
if (tasks) {
for (const task of tasks) {
generateNewProjectTaskTimeLineRow(timelinerow,expandedschedulingUnitItems,task,"project_task_group");
generateNewProjectTaskTimeLineRow(timelinerow, expandedschedulingUnitItems, task, "project_task_group");
}
}
}
}
function generateNewProjectTaskTimeLineRow(schedulingunitTimelinerow, expandedschedulingUnitItems, task, groupingkey) {
let newtimelineItem = { ...schedulingunitTimelinerow }
newtimelineItem[groupingkey] = schedulingunitTimelinerow.project +newtimelineItem.groupingSuffix+"#tasks";
newtimelineItem[groupingkey] = schedulingunitTimelinerow.project + newtimelineItem.groupingSuffix + "#tasks";
newtimelineItem.name = newtimelineItem.name + " - " + task.name
newtimelineItem.id = newtimelineItem.id + newtimelineItem[groupingkey] + task.id;
newtimelineItem.task = task;
......@@ -76,9 +76,9 @@ function generateNewProjectTaskTimeLineRow(schedulingunitTimelinerow, expandedsc
const endTimeWithDisplayDate = moment(`${newtimelineItem.displayDate.format('YYYY-MM-DD')} ${task.process_stop_time?.split('T')[1]}`)
const onSkyStartTimeWithDisplayDate = moment(`${newtimelineItem.displayDate.format('YYYY-MM-DD')} ${task.on_sky_start_time?.split('T')[1]}`);
const onSkyEndTimeWithDisplayDate = moment(`${newtimelineItem.displayDate.format('YYYY-MM-DD')} ${task.on_sky_stop_time?.split('T')[1]}`)
newtimelineItem.start_time =newtimelineItem.SkyTimesOnTimes ? onSkyStartTimeWithDisplayDate : startTimeWithDisplayDate;
newtimelineItem.end_time= newtimelineItem.SkyTimesOnTimes ? onSkyEndTimeWithDisplayDate : endTimeWithDisplayDate;
newtimelineItem.start_time = newtimelineItem.SkyTimesOnTimes ? onSkyStartTimeWithDisplayDate : startTimeWithDisplayDate;
newtimelineItem.end_time = newtimelineItem.SkyTimesOnTimes ? onSkyEndTimeWithDisplayDate : endTimeWithDisplayDate;
newtimelineItem.duration = duration;
newtimelineItem.desc = task.description;
......@@ -100,9 +100,8 @@ function generateNewStationGroupTimeLineRow(schedulingunitTimelinerow, expandeds
function generateNewStationTimeLineRow(schedulingunitTimelinerow, expandedschedulingUnitItems, station, groupingkey) {
let newtimelineItem = { ...schedulingunitTimelinerow }
newtimelineItem[groupingkey] = station + newtimelineItem.groupingSuffix+"#station";
newtimelineItem.isSingleStation=true;
newtimelineItem[groupingkey] = station + newtimelineItem.groupingSuffix + "#station";
newtimelineItem.isSingleStation = true;
newtimelineItem.id = newtimelineItem.id + newtimelineItem[groupingkey];
cleanSingleTimelineGroups(newtimelineItem);
expandedschedulingUnitItems.push(newtimelineItem);
......
......@@ -24,12 +24,12 @@ export function getGroups(itemGroupKey, startTime, data) {
if (itemGroupKey =="group") { // then group is the default, date one
groups = createDateGroups('', startTime);
} else {
groups = createGroups(data, itemGroupKey, startTime)
groups = createGroups(data, itemGroupKey )
}
return { groups, itemGroupKey }
}
export function createGroups(timeLineRows, property, startTime) {
export function createGroups(timeLineRows, property ) {
const groups = [];
if (timeLineRows !== undefined && timeLineRows.length>0) {
......@@ -43,12 +43,17 @@ export function createGroups(timeLineRows, property, startTime) {
let isSingleStation = false;
if (splittedElement.length>2) {
if (splittedElement[2]=="station") isSingleStation = true;
}
const groupDate = moment(datepart)
const weeknr = groupDate.format("w")
const weekdate = groupDate.format("MMM DD - ddd")
const formatedDate = groupDate.format(UIConstants.CALENDAR_GROUP_FORMAT)
groups.push({ id: element, title:splittedElement[0], date: groupDate, formatedDate: formatedDate, weeknr: weeknr, weekdate: weekdate, isSingleStation:isSingleStation });
const newgroup = { id: element, title:splittedElement[0], date: groupDate, formatedDate: formatedDate, weeknr: weeknr, weekdate: weekdate, isSingleStation:isSingleStation };
if (isSingleStation) {
newgroup.designatedStation=splittedElement[0];
}
groups.push(newgroup);
}
}
......
......@@ -14,7 +14,7 @@ import moment from "moment";
import { ProgressSpinner } from 'primereact/progressspinner';
import { fetchAllOptionsForMultiSelects } from "../../data/filters.data";
const isDebugLoggingOn = false
function Toggle(props) { //TODO: extract to separate class
const {
labelName,
......@@ -36,8 +36,8 @@ function Toggle(props) { //TODO: extract to separate class
</div>
}
function filterReservations(reservations, visibleStartTime, reservationFilter, projectFilter) {
let reservationItems = reservations.flatMap(reservation => splitObjectIfSpanIsMultipleDays(reservation, false))
function filterReservations(reservations, visibleStartTime, reservationFilter, projectFilter,endTime,startTime) {
let reservationItems = reservations.flatMap(reservation => splitObjectIfSpanIsMultipleDays(reservation, false,startTime,endTime))
.map((reservation, index) => getReservationItem(reservation, index, moment(visibleStartTime).format(UIConstants.CALENDAR_DEFAULTDATE_FORMAT)))
.filter(reservationItem => reservationFilter.includes(reservationItem.reason))
......@@ -75,6 +75,8 @@ function filterSchedulingUnits(schedulingUnits, visibleStartTime, onSkyToggle, s
export default function Filters(props) {
const {
visibleStartTime,
endTime,
startTime,
setItems,
data,
setRefetchToggle,
......@@ -159,7 +161,9 @@ export default function Filters(props) {
let reservationsItems = []
if (reservationsToggle) {
reservationsItems = filterReservations(data.reservations, visibleStartTime, reservationFilter, projectFilter);
if(isDebugLoggingOn) console.log("Gather Reservations for ",visibleStartTime,endTime,startTime);
reservationsItems = filterReservations(data.reservations, visibleStartTime, reservationFilter, projectFilter,endTime,startTime);
if (isDebugLoggingOn) console.log("Reservation sais",reservationsItems)
}
setItems(prevState => ({
......
......@@ -66,11 +66,28 @@ export async function fetchSunTimings(startTime, setItems,groups,groupkey) {
export async function fetchLSTStationShift(utcTime,station) {
let UTCTimeNow = moment(utcTime).format(UIConstants.UTC_DATE_TIME_FORMAT);
let lstresponse = await UtilService.getLST(UTCTimeNow, [station,UIConstants.TIMINGSERVER]) // we get timing also, makes debugging a lot easier.
let lst = lstresponse.data?.LST?.[station];
let UTCAlsoTimeNow = moment.utc(lstresponse.data?.UTC).format(UIConstants.UTC_DATE_TIME_FORMAT);
const shift = GetLstShiftinSeconds(lst, UTCAlsoTimeNow)
return shift
let stations = [UIConstants.TIMINGSERVER]
if (station) {
if (station !==UIConstants.TIMINGSERVER){
stations = [station,UIConstants.TIMINGSERVER]
}
else {
station = UIConstants.TIMINGSERVER;
stations = [UIConstants.TIMINGSERVER]
}
}
let lstresponse = await UtilService.getLST(UTCTimeNow, stations) // we get timing also, makes debugging a lot easier.
if (!lstresponse) return -1;
let lststation = lstresponse.data?.LST?.[station];
let UTCAlsoTimeNow = moment.utc(lstresponse.data?.UTC).format(UIConstants.UTC_DATE_TIME_FORMAT);
let lstcore = lstresponse.data?.LST?.[UIConstants.TIMINGSERVER];
const shiftLSTStation = GetLstShiftinSeconds(lststation, UTCAlsoTimeNow)
const shiftLSTCore = GetLstShiftinSeconds(lstcore, UTCAlsoTimeNow)
return { shiftLSTStation, shiftLSTCore }
}
export async function fetchLSTShift(startTime, setHeaderSettings) {
......@@ -79,7 +96,7 @@ export async function fetchLSTShift(startTime, setHeaderSettings) {
setHeaderSettings(prevState => ({
...prevState,
lstShiftInSeconds: shift
lstShiftInSeconds: shift.shiftLSTCore
}))
}
......@@ -91,29 +108,19 @@ export function GetLstShiftinSeconds(lst, UTCTimeNow) {
return shift;
}
async function addNestedBlueprintInformation(suBluePrint) {
let missingStations = []
for (let taskIndex = 0; taskIndex < suBluePrint.task_blueprints.length; taskIndex++) {
const task = suBluePrint.task_blueprints[taskIndex]
let subtasksSpecifications = []
for (const subtaskId of task.subtasks_ids) {
subtasksSpecifications.push(await TaskService.getSubtaskDetails(subtaskId, "specifications_doc"))
const foundMissingStations = await TaskService.getSubtaskMissingStations(subtaskId);
missingStations = missingStations.concat(foundMissingStations.filter((item) => missingStations.indexOf(item) < 0))
}
suBluePrint.task_blueprints[taskIndex].subtasks = subtasksSpecifications
}
const timelineCommonUtils = new TimelineCommonUtils();
suBluePrint.stations = timelineCommonUtils.getSUStations(suBluePrint);
suBluePrint.missingStations = missingStations
const projectInfo = await ScheduleService.getSchedulingSet(suBluePrint.draft.scheduling_set_id, "project_id")
suBluePrint.project = projectInfo.project_id;
// we get the missing_stations and the stations from the already present in memory information from the timeline view
// The Deeper subtasks information is removed.
async function addNestedBlueprintInformation(suBluePrint,inMemoryItem) {
let missingStations = inMemoryItem.missing_stations;
suBluePrint.stations = inMemoryItem.specified_stations;
suBluePrint.missingStations = missingStations
suBluePrint.project = inMemoryItem.project;
}
export async function fetchSUSummaryInformation(id, setSummarySettings) {
export async function fetchSUSummaryInformation(id, setSummarySettings, inMemoryItem) {
let suBluePrint = await ScheduleService.getBlueprintByschedulingUnitId(id, "task_blueprints,draft")
await addNestedBlueprintInformation(suBluePrint);
await addNestedBlueprintInformation(suBluePrint,inMemoryItem);
const suConstraintTemplate = await ScheduleService.getSchedulingConstraintTemplateById(suBluePrint.scheduling_constraints_template_id)
......
......@@ -105,7 +105,7 @@ function renderSUItem({
itemContext,
getItemProps
}, setPopPositionCallback, setMouseOverItemCallback, setSummaryItemCallback) {
const scheduleMethodBorder = item.scheduler === "dynamic" ? "1.5px dashed black" : "1.5px solid black"
const scheduleMethodBorder = item.scheduler === "dynamic" ? "1.5px dashed black " : "1.5px solid black"
let itemDivStyle = getItemDivStyle(itemContext, item);
itemDivStyle.fontWeight = 600
itemDivStyle.border = scheduleMethodBorder
......@@ -117,7 +117,7 @@ function renderSUItem({
style: itemDivStyle,
className: `${item.status ? 'su-' + item.status : ''}`,
})}
onMouseDown={() => setSummaryItemCallback({ id: item.suId, type: item.type })}
onMouseDown={() => setSummaryItemCallback({ id: item.suId, type: item.type,item:item })}
onMouseOver={(evt) => {
hoverOverItem(evt, item, setPopPositionCallback, setMouseOverItemCallback)
}}
......@@ -187,11 +187,11 @@ export function groupRenderer({ group }) {
<tr><td>UTC</td><td> {group.cursorInfo.utc}</td></tr>
{group.isSingleStation ? (
<tr>
<td>LST Station </td>
<td>{group.cursorInfo.stationlst}</td>
<td>LST {group.cursorInfo.stationName || 'Station' } </td>
<td>{group.cursorInfo.stationLST}</td>
</tr>
) : null}
<tr><td>LST Core </td><td>{group.cursorInfo.corelst}</td></tr>
<tr><td>LST Core </td><td>{group.cursorInfo.coreLST}</td></tr>
</tbody>
</table> : null}
</div>;
......
......@@ -21,9 +21,9 @@ function splitElementPerDay(element, spanInDays, shouldShowOnSkyTimes) {
const newElements = []
for (let day = 0; day <= spanInDays; day++) {
let newElement = { ...element }
const startTime = (shouldShowOnSkyTimes ? (element.on_sky_start_time ?? element.process_start_time) : element.process_start_time) ?? element.start_time
const endTime = (shouldShowOnSkyTimes ? (element.on_sky_stop_time ?? element.process_stop_time) : element.process_stop_time) ?? element.stop_time
const endTime = (shouldShowOnSkyTimes ? (element.on_sky_stop_time ?? element.process_stop_time) : element.process_stop_time) ?? element.stop_time
const { formattedStart, formattedEnd } = determineNewStartAndEnd(startTime, day, spanInDays, endTime);
if (shouldShowOnSkyTimes) {
......@@ -159,7 +159,6 @@ export function getTimelineItem(suBlueprint, displayDate, includeStationsInfo, i
priority_queue: suBlueprint.priority_queue,
priority_queue_group: suBlueprint.priority_queue + '#' + currentGrouping,
unschedulable_reason: suBlueprint.unschedulable_reason,
id: `${suBlueprint.id}-${currentGroup}`,
title: "",
group: currentGroup,
......@@ -218,18 +217,17 @@ export function getReservationItem(reservation, index, displayDate) {
const startTime = moment(displayDate + ` ${moment(reservation.start_time).format('HH:mm:ss')}`);
const endTime = moment(displayDate + ` ${moment(reservation.stop_time).format('HH:mm:ss')}`);
const type = specificationsDoc?.activity?.type;
const group = moment(reservation.start_time).format(UIConstants.CALENDAR_GROUP_FORMAT);
const blockColor = getReservationTypeColorIndex(specificationsDoc?.schedulability);
const duration = reservation.duration ? UnitConverter.getSecsToHHmmss(reservation.duration) : "Unknown";
const stations = getStationsInfo(specificationsDoc?.resources?.specified_stations);
const stations = getStationsInfo(specificationsDoc?.resources?.stations);
const currentGroup = moment(reservation.start_time).format(UIConstants.CALENDAR_GROUP_FORMAT)
const currentGrouping = moment(reservation.start_time).format(UIConstants.CALENDAR_DEFAULTDATE_FORMAT)
let timeLineRow = {
id: reservation.id + "~" + index,
reservationId: reservation.id,
start_time: startTime,
end_time: endTime,
group: group,
group: currentGroup,
name: reservation.name,
project: reservation.project_id,
......@@ -248,9 +246,10 @@ export function getReservationItem(reservation, index, displayDate) {
selectedBgColor: blockColor.bgColor,
color: blockColor.color
};
const currentGrouping = moment(timeLineRow.start_time).format(UIConstants.CALENDAR_DEFAULTDATE_FORMAT)
timeLineRow.specified_stations = reservation.stations;
timeLineRow.name_group = timeLineRow.name + '#' + currentGrouping
timeLineRow.project_group = timeLineRow.project + '#' + currentGrouping
timeLineRow.project_task_group = timeLineRow.project + '#' + currentGrouping ; // we do not have tasks, but we need to be visible.
......
......@@ -156,12 +156,7 @@ describe("splitObjectIfSpanIsMultipleDays", () => {
});
const STATUS_COLORS = {
"ERROR": "FF0000", "CANCELLED": "#00FF00", "DEFINED": "#00BCD4", "UNSCHEDULABLE": "#9e0707",
"SCHEDULABLE": "#0000FF", "SCHEDULED": "#abc", "OBSERVING": "#bcd",
"OBSERVED": "#cde", "PROCESSING": "#cddc39", "PROCESSED": "#fed",
"INGESTING": "#edc", "FINISHED": "#47d53d"
};
describe("getTimelineItem", () => {
const tasks = [
{
......
......@@ -200,31 +200,35 @@ export async function getCursorDateTimes(cursorTimeUTC, groupIndex, groupDate, l
const cursorTime = moment(cursorTimeUTC)
const cursorDateTimeUTC = moment(groupDate).set({ hour: cursorTime.get('hour'), minute: cursorTime.get('minute'), second: cursorTime.get('second') });
let utc = cursorDateTimeUTC.format("HH:mm");
const cursorDateTimeCoreLST = moment(cursorDateTimeUTC).add(lstShiftInSeconds, 'second');
let corelst = cursorDateTimeCoreLST.format("HH:mm");
let stationlst = await GetStationLst(isSingleStation, cursorDateTimeUTC, stationName);
let cursordatetimes = { utc, corelst, stationlst }
setCursorDateTimes(cursordatetimes)
let shifts = await GetStationLst(isSingleStation, cursorDateTimeUTC, stationName);
let coreLST = shifts.coreLST;
let stationLST = shifts.stationLST;
let cursordatetimes = { utc, coreLST, stationLST, stationName:stationName }
setCursorDateTimes(cursordatetimes)
}
async function GetStationLst(isSingleStation, cursorDateTimeUTC, stationName) {
if (!isSingleStation) return;
let lststationshiftSeconds = 0;
async function GetStationLst(isSingleStation, cursorDateTimeUTC, stationName = UIConstants.TIMINGSERVER) {
let lststationshiftSeconds = -2;
let shift;
let key = cursorDateTimeUTC.format(UIConstants.CALENDAR_GROUPING_FORMAT) + "_" + stationName; // we cache on a daily base
if (key in timecache) {
if (!timecache[key].isRetrieved) { return "...";}
lststationshiftSeconds = timecache[key].shift;
}
else {
timecache[key] = {isRetrieved:false};
lststationshiftSeconds = await fetchLSTStationShift(cursorDateTimeUTC, stationName);
timecache[key] = {isRetrieved:true, shift:lststationshiftSeconds};
if (!timecache[key].isRetrieved) { return "..."; }
shift = timecache[key].shift;
lststationshiftSeconds = shift.shiftLSTStation;
}
lststationshiftSeconds = moment(cursorDateTimeUTC).add(lststationshiftSeconds, 'second');
const stationlst = lststationshiftSeconds.format("HH:mm");
return stationlst;
if (lststationshiftSeconds == -2) {
timecache[key] = { isRetrieved: false };
shift = await fetchLSTStationShift(cursorDateTimeUTC, stationName);
timecache[key] = { isRetrieved: true, shift };
}
const stationLST = moment(cursorDateTimeUTC).add(shift.shiftLSTStation, 'second').format("HH:mm");;
const coreLST = moment(cursorDateTimeUTC).add(shift.shiftLSTCore, 'second').format("HH:mm");;
return { stationLST, coreLST };
}
......
......@@ -55,7 +55,7 @@ const UtilService = {
localStorage.setItem('UTC_LST_MAP_V3' + stationstring, JSON.stringify(localUtcLstMap));
return response;
} catch (error) {
console.error(error);
return null;
}
},
/** Function to fetch sun timings from the backend for single station. */
......
......@@ -5,18 +5,20 @@ import useWebSocket from 'react-use-websocket';
import { getStore } from "../services/store.helper";
import UIConstants from "../utils/ui.constants";
function useWeekViewWebSocket(data, setData, summaryItem, setSummaryItem, setShowSummary) {
const isDebugLoggingOn =false;
function useWeekViewWebSocket(data, setData, summaryItem, setSummaryItem, setShowSummary) {
/**
* Function to call when websocket is connected
*/
function onConnect() {
try {
console.log("WS Opened");
if (isDebugLoggingOn) console.log("WS Opened");
const userDets = localStorage.getItem("user");
if (userDets) {
sendMessage(JSON.stringify({ "token": JSON.parse(userDets).websocket_token }));
console.log("Auth token submitted");
if (isDebugLoggingOn) console.log("Auth token submitted");
}
} catch (err) {
console.log('err', err);
......@@ -27,7 +29,7 @@ function useWeekViewWebSocket(data, setData, summaryItem, setSummaryItem, setSho
* Function to call when websocket is disconnected
*/
function onDisconnect() {
console.log("WS Closed");
if (isDebugLoggingOn) console.log("WS Closed");
}
function fetchBlueprintAndAddToTimeline(id) {
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment