Used queries to get data from scrape database instead of FabTime to use a single data source.
This commit is contained in:
@ -8,12 +8,19 @@ namespace ReportingServices.Shared.HelperClasses
|
||||
{
|
||||
public static async Task<T> GetApi<T>(string url)
|
||||
{
|
||||
T deserializedJson;
|
||||
T deserializedJson = default(T);
|
||||
|
||||
using (HttpClient client = new())
|
||||
try
|
||||
{
|
||||
string apiResponse = await client.GetStringAsync(url);
|
||||
deserializedJson = JsonSerializer.Deserialize<T>(apiResponse);
|
||||
using (HttpClient client = new())
|
||||
{
|
||||
string apiResponse = await client.GetStringAsync(url);
|
||||
deserializedJson = JsonSerializer.Deserialize<T>(apiResponse);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
return deserializedJson;
|
||||
|
@ -11,10 +11,67 @@ namespace ReportingServices.Shared.HelperClasses
|
||||
|
||||
public static DailyReport SetUpDailyReport(ILogger logger, string baseUrlFabtime, string baseUrlScrapeDb)
|
||||
{
|
||||
List<string> cleanTools = new()
|
||||
{
|
||||
"AHPS",
|
||||
"AKRION1",
|
||||
"CB3",
|
||||
"MES",
|
||||
"SRD 1",
|
||||
"SRD 2"
|
||||
};
|
||||
|
||||
List<string> metrologyTools = new()
|
||||
{
|
||||
"ASET",
|
||||
"BIORAD2",
|
||||
"BIORAD3",
|
||||
"BIORAD4",
|
||||
"BIORAD5",
|
||||
"CDE2",
|
||||
"CDE3",
|
||||
"CDE5",
|
||||
"FLEXUS",
|
||||
"HGCV1",
|
||||
"HGCV2",
|
||||
"HGCV3",
|
||||
"SRP"
|
||||
};
|
||||
|
||||
List<Task<List<EquipmentStateByDay>>> tasksEQState = new();
|
||||
List<Task<List<ToolStateCurrent>>> tasksState = new();
|
||||
DailyReport report = new();
|
||||
|
||||
List<Reactor> reactors = ApiCaller.GetApi<List<Reactor>>(baseUrlScrapeDb + "Reactors").Result;
|
||||
|
||||
List<Task<List<ReactorEvent>>> toolEvents = new();
|
||||
List<Task<ToolEvent>> cleanEvents = new();
|
||||
List<Task<ToolEvent>> metrologyEvents = new();
|
||||
|
||||
try
|
||||
{
|
||||
foreach (Reactor reactor in reactors)
|
||||
{
|
||||
toolEvents.Add(ApiCaller.GetApi<List<ReactorEvent>>(baseUrlScrapeDb + "ReactorEvents?startDate=" + report.StartDate.ToString() +
|
||||
"&endDate=" + DateTime.Now.ToString() + "&reactorNumber=" + reactor.ReactorNumber + "&reactorType=" + reactor.Type));
|
||||
}
|
||||
|
||||
foreach (string tool in cleanTools)
|
||||
{
|
||||
cleanEvents.Add(ApiCaller.GetApi<ToolEvent>(baseUrlScrapeDb + "ToolEvents?toolID=" + tool));
|
||||
}
|
||||
|
||||
foreach (string tool in metrologyTools)
|
||||
{
|
||||
metrologyEvents.Add(ApiCaller.GetApi<ToolEvent>(baseUrlScrapeDb + "ToolEvents?toolID=" + tool));
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
try
|
||||
{
|
||||
report.SLLTools = JsonFileHandler.LoadJSONFile<List<SLLTool>>(_SLLFilePath);
|
||||
@ -34,8 +91,8 @@ namespace ReportingServices.Shared.HelperClasses
|
||||
|
||||
try
|
||||
{
|
||||
task1 = ApiCaller.GetApi<YieldInformation>(baseUrlFabtime + "ReactorOuts?startDate=" + report.StartDate.ToString() + "&endDate=" + DateTime.Now.ToString());
|
||||
task2 = ApiCaller.GetApi<YieldInformation>(baseUrlFabtime + "ReactorOuts?startDate=" + report.StartDate.AddDays(-7).ToString() + "&endDate=" + report.StartDate.ToString());
|
||||
task1 = ApiCaller.GetApi<YieldInformation>(baseUrlScrapeDb + "ReactorOuts?startDate=" + report.StartDate.ToString() + "&endDate=" + DateTime.Now.ToString());
|
||||
task2 = ApiCaller.GetApi<YieldInformation>(baseUrlScrapeDb + "ReactorOuts?startDate=" + report.StartDate.AddDays(-7).ToString() + "&endDate=" + report.StartDate.ToString());
|
||||
|
||||
tasksEQState.Add(ApiCaller.GetApi<List<EquipmentStateByDay>>(baseUrlFabtime + "ToolStateTrend?toolType=ASM"));
|
||||
tasksEQState.Add(ApiCaller.GetApi<List<EquipmentStateByDay>>(baseUrlFabtime + "ToolStateTrend?toolType=EPP"));
|
||||
@ -53,13 +110,12 @@ namespace ReportingServices.Shared.HelperClasses
|
||||
|
||||
Task<QuarterlyTargets> targets = null;
|
||||
Task<List<RDS>> rds = null;
|
||||
Task<List<Reactor>> reactors = null;
|
||||
|
||||
try
|
||||
{
|
||||
targets = ApiCaller.GetApi<QuarterlyTargets>(baseUrlScrapeDb + "Targets");
|
||||
rds = ApiCaller.GetApi<List<RDS>>(baseUrlScrapeDb + "RDS?date=" + report.StartDate.ToString());
|
||||
reactors = ApiCaller.GetApi<List<Reactor>>(baseUrlScrapeDb + "Reactors");
|
||||
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@ -86,9 +142,29 @@ namespace ReportingServices.Shared.HelperClasses
|
||||
try
|
||||
{
|
||||
report.QuarterlyTargets = targets.Result;
|
||||
|
||||
report.SetRDSInfo(rds.Result);
|
||||
report.SetReactorInfo(reactors.Result, GetUnscheduledReactors(report));
|
||||
report.SetReactorInfo(reactors, GetUnscheduledReactors(report));
|
||||
|
||||
foreach (var task in toolEvents)
|
||||
{
|
||||
report.ToolEvents.Add(new ToolEventView(task.Result,
|
||||
report.StartDate.ToString(), DateTime.Now.ToString(), task.Result[0].REACT_NO,
|
||||
reactors.FirstOrDefault(x => x.ReactorNumber == int.Parse(task.Result[0].REACT_NO)).Type));
|
||||
}
|
||||
|
||||
report.ToolEvents = report.ToolEvents
|
||||
.Where(x => x.Reactor != "100" && x.Reactor != "101" && x.Reactor != "47")
|
||||
.OrderBy(x => x.Reactor)
|
||||
.ToList();
|
||||
|
||||
foreach (Task<ToolEvent> task in cleanEvents)
|
||||
report.CleanEvents.Add(task.Result);
|
||||
|
||||
foreach (Task<ToolEvent> task in metrologyEvents)
|
||||
report.MetrologyEvents.Add(task.Result);
|
||||
|
||||
report.CleanEvents = report.CleanEvents.Where(x => (x.TOOL_MODE != "PROD" || x.TOOL_MODE_DESC != "Production") && x.TOOL_MODE != "OUT").ToList();
|
||||
report.MetrologyEvents = report.MetrologyEvents.Where(x => (x.TOOL_MODE != "PROD" || x.TOOL_MODE_DESC != "Production") && x.TOOL_MODE != "OUT").ToList();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
@ -0,0 +1,16 @@
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace ReportingServices.Shared.Models.ProductionReport
|
||||
{
|
||||
public class ReactorEvent
|
||||
{
|
||||
[JsonPropertyName("REACT_NO")]
|
||||
public string REACT_NO { get; set; }
|
||||
[JsonPropertyName("EVENT_DTM")]
|
||||
public string EVENT_DTM { get; set; }
|
||||
[JsonPropertyName("COMMENT")]
|
||||
public string COMMENT { get; set; }
|
||||
[JsonPropertyName("REACT_MODE")]
|
||||
public string REACT_MODE { get; set; }
|
||||
}
|
||||
}
|
@ -0,0 +1,16 @@
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace ReportingServices.Shared.Models.ProductionReport
|
||||
{
|
||||
public class ToolEvent
|
||||
{
|
||||
[JsonPropertyName("TOOL_ID")]
|
||||
public string TOOL_ID { get; set; }
|
||||
[JsonPropertyName("START_DTM")]
|
||||
public string START_DTM { get; set; }
|
||||
[JsonPropertyName("TOOL_MODE")]
|
||||
public string TOOL_MODE { get; set; }
|
||||
[JsonPropertyName("TOOL_MODE_DESC")]
|
||||
public string TOOL_MODE_DESC { get; set; }
|
||||
}
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
namespace ReportingServices.Shared.Models.ProductionReport
|
||||
{
|
||||
public class ToolUptimeData
|
||||
{
|
||||
public string Date { get; set; }
|
||||
public double UptimePercentage { get; set; }
|
||||
}
|
||||
}
|
@ -269,5 +269,133 @@ namespace ReportingServices.Shared.Repositories
|
||||
|
||||
return rdsList;
|
||||
}
|
||||
|
||||
public List<ReactorOutsByRDS> GetRDSRunBetweenDates(string startDate, string endDate)
|
||||
{
|
||||
List<ReactorOutsByRDS> outs = new();
|
||||
|
||||
OpenConnection();
|
||||
|
||||
SqlCommand cmd = _connection.CreateCommand();
|
||||
|
||||
string query = "SELECT SEQ, WFRS_OUT, DATE_OUT " +
|
||||
" FROM RDS " +
|
||||
" WHERE DATE_OUT >= @startDate " +
|
||||
" AND DATE_OUT < @endDate " +
|
||||
"ORDER BY DATE_OUT ASC";
|
||||
|
||||
cmd.CommandText = query;
|
||||
cmd.Parameters.AddWithValue("@startDate", startDate);
|
||||
cmd.Parameters.AddWithValue("@endDate", endDate);
|
||||
|
||||
using (SqlDataReader reader = cmd.ExecuteReader())
|
||||
{
|
||||
while (reader.Read())
|
||||
outs.Add(new ReactorOutsByRDS
|
||||
{
|
||||
RDS_NO = reader[0].ToString(),
|
||||
Units = reader[1].ToString(),
|
||||
EndProcessTime = reader[2].ToString()
|
||||
});
|
||||
}
|
||||
|
||||
cmd.Dispose();
|
||||
|
||||
CloseConnection();
|
||||
|
||||
return outs;
|
||||
}
|
||||
|
||||
public List<ReactorEvent> GetReactorEvents(string startDate, string endDate, string reactorNumber)
|
||||
{
|
||||
List<ReactorEvent> events = new();
|
||||
|
||||
OpenConnection();
|
||||
|
||||
SqlCommand cmd = _connection.CreateCommand();
|
||||
|
||||
string query = "SELECT " +
|
||||
" REACT_NO, " +
|
||||
" EVENT_DTM, " +
|
||||
" COMMENT, " +
|
||||
" REACT_MODE " +
|
||||
" FROM REACT_EVENT " +
|
||||
" WHERE EVENT_DTM > @startDate " +
|
||||
" AND EVENT_DTM < @endDate " +
|
||||
" AND REACT_NO = @reactorNumber " +
|
||||
"UNION ALL " +
|
||||
"SELECT " +
|
||||
" REACT_NO, " +
|
||||
" EVENT_DTM, " +
|
||||
" COMMENT, " +
|
||||
" REACT_MODE " +
|
||||
" FROM " +
|
||||
" (SELECT TOP 1 * FROM REACT_EVENT " +
|
||||
" WHERE EVENT_DTM < @startDate " +
|
||||
" AND REACT_NO = @reactorNumber ORDER BY EVENT_DTM DESC) AS tbl1 " +
|
||||
"ORDER BY EVENT_DTM ASC;";
|
||||
|
||||
cmd.CommandText = query;
|
||||
cmd.Parameters.AddWithValue("@startDate", startDate);
|
||||
cmd.Parameters.AddWithValue("@endDate", endDate);
|
||||
cmd.Parameters.AddWithValue("@reactorNumber", reactorNumber);
|
||||
|
||||
using (SqlDataReader reader = cmd.ExecuteReader())
|
||||
{
|
||||
while (reader.Read())
|
||||
events.Add(new ReactorEvent
|
||||
{
|
||||
REACT_NO = reader[0].ToString(),
|
||||
EVENT_DTM = reader[1].ToString(),
|
||||
COMMENT = reader[2].ToString(),
|
||||
REACT_MODE = reader[3].ToString()
|
||||
});
|
||||
}
|
||||
|
||||
cmd.Dispose();
|
||||
|
||||
CloseConnection();
|
||||
|
||||
return events;
|
||||
}
|
||||
|
||||
public ToolEvent GetLatestToolEvent(string toolID)
|
||||
{
|
||||
ToolEvent evnt = new();
|
||||
|
||||
OpenConnection();
|
||||
|
||||
SqlCommand cmd = _connection.CreateCommand();
|
||||
|
||||
string query = "SELECT TOP 1 " +
|
||||
" TOOL_ID, " +
|
||||
" START_DTM, " +
|
||||
" TOOL_MODE, " +
|
||||
" TOOL_MODE_DESC " +
|
||||
" FROM TOOL_LOG " +
|
||||
" WHERE TOOL_ID = @toolID " +
|
||||
"ORDER BY START_DTM DESC;";
|
||||
|
||||
cmd.CommandText = query;
|
||||
cmd.Parameters.AddWithValue("@toolID", toolID);
|
||||
|
||||
using (SqlDataReader reader = cmd.ExecuteReader())
|
||||
{
|
||||
while (reader.Read())
|
||||
evnt = new ToolEvent
|
||||
{
|
||||
TOOL_ID = reader[0].ToString(),
|
||||
START_DTM = reader[1].ToString(),
|
||||
TOOL_MODE = reader[2].ToString(),
|
||||
TOOL_MODE_DESC = reader[3].ToString()
|
||||
};
|
||||
}
|
||||
|
||||
cmd.Dispose();
|
||||
|
||||
CloseConnection();
|
||||
|
||||
return evnt;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -13,5 +13,8 @@ namespace ReportingServices.Shared.Repositories
|
||||
public QuarterlyTargets GetQuarterlyTargets();
|
||||
public List<Reactor> GetReactors();
|
||||
public List<RDS> GetRDSForLastDay(string date);
|
||||
public List<ReactorOutsByRDS> GetRDSRunBetweenDates(string startDate, string endDate);
|
||||
public List<ReactorEvent> GetReactorEvents(string startDate, string endDate, string reactorNumber);
|
||||
public ToolEvent GetLatestToolEvent(string toolID);
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,5 @@
|
||||
using ReportingServices.Shared.HelperClasses;
|
||||
using ReportingServices.Shared.Models.ProductionReport;
|
||||
using ReportingServices.Shared.ViewModels.ProductionReport;
|
||||
|
||||
namespace ReportingServices.Shared.ViewModels.ProductionReport
|
||||
{
|
||||
@ -9,8 +8,11 @@ namespace ReportingServices.Shared.ViewModels.ProductionReport
|
||||
public DateTime StartDate { get; set; }
|
||||
public YieldStatistics CurrentWeek { get; set; }
|
||||
public YieldStatistics PreviousWeek { get; set; }
|
||||
public List<ToolEventView> ToolEvents { get; set; }
|
||||
public Dictionary<string, List<EquipmentStateByDay>> ToolAvailibilityByType { get; set; }
|
||||
public Dictionary<string, ToolStateByType> ToolStateByType { get; set; }
|
||||
public List<ToolEvent> CleanEvents { get; set; }
|
||||
public List<ToolEvent> MetrologyEvents { get; set; }
|
||||
public Dictionary<string, List<string>> ToolStatesByOwner { get; set; }
|
||||
public Dictionary<string, List<int>> DualLayerReactors { get; set; }
|
||||
public ManualReportEntries ManualReportEntries { get; set; }
|
||||
@ -26,8 +28,11 @@ namespace ReportingServices.Shared.ViewModels.ProductionReport
|
||||
{
|
||||
ToolAvailibilityByType = new();
|
||||
ToolStateByType = new();
|
||||
CleanEvents = new();
|
||||
MetrologyEvents = new();
|
||||
DualLayerReactors = new();
|
||||
UnloadTempsByDay = new();
|
||||
ToolEvents = new();
|
||||
StartDate = DateTime.Parse(APIHelperFunctions.GetBeginningOfWeekAsAPIString());
|
||||
CurrentWeek = new(StartDate, true);
|
||||
PreviousWeek = new(StartDate.AddDays(-7), false);
|
||||
|
@ -0,0 +1,187 @@
|
||||
using ReportingServices.Shared.Models.ProductionReport;
|
||||
using System.Data;
|
||||
|
||||
namespace ReportingServices.Shared.ViewModels.ProductionReport
|
||||
{
|
||||
public class ToolEventView
|
||||
{
|
||||
public string Reactor { get; set; }
|
||||
public string Type { get; set; }
|
||||
public bool DownMoreThanTwelveHours { get; set; }
|
||||
public string StartDate { get; set; }
|
||||
public string EndDate { get; set; }
|
||||
public bool IsInProduction { get; set; }
|
||||
public List<ReactorEvent> Events { get; set; }
|
||||
public ReactorEvent MostRecentEvent { get; set; }
|
||||
public List<ToolUptimeData> Uptime { get; set; }
|
||||
|
||||
public ToolEventView(List<ReactorEvent> events, string startDate, string endDate, string reactor, string type)
|
||||
{
|
||||
Events = events;
|
||||
StartDate = startDate;
|
||||
EndDate = endDate;
|
||||
Reactor = reactor;
|
||||
Type = type;
|
||||
MostRecentEvent = events[events.Count - 1];
|
||||
IsInProduction = EventIsProduction(MostRecentEvent.REACT_MODE);
|
||||
DownMoreThanTwelveHours = DetermineIfExtendedDowntime();
|
||||
Uptime = DetermineToolUptimeData();
|
||||
}
|
||||
|
||||
private bool DetermineIfExtendedDowntime()
|
||||
{
|
||||
double numberOfHours = 0;
|
||||
|
||||
for (int i = Events.Count - 1; i >= 0; i--)
|
||||
{
|
||||
if (EventIsProduction(Events[i].REACT_MODE))
|
||||
return false;
|
||||
|
||||
if (i == Events.Count - 1)
|
||||
numberOfHours = (DateTime.Now - DateTime.Parse(Events[i].EVENT_DTM)).TotalHours;
|
||||
else
|
||||
numberOfHours += (DateTime.Parse(Events[i + 1].EVENT_DTM) - DateTime.Parse(Events[i].EVENT_DTM)).TotalHours;
|
||||
|
||||
if (numberOfHours > 12)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public List<ToolUptimeData> DetermineToolUptimeData()
|
||||
{
|
||||
List<ToolUptimeData> data = new();
|
||||
|
||||
bool currentModeIsUp;
|
||||
double uptime = 0;
|
||||
DateTime compareDate = DateTime.Parse(StartDate).Date;
|
||||
DateTime previousTransaction = DateTime.Parse(StartDate).Date;
|
||||
|
||||
currentModeIsUp = EventIsProduction(Events[0].REACT_MODE);
|
||||
|
||||
if (Events.Count == 1)
|
||||
data = LoadUptimeData(currentModeIsUp);
|
||||
|
||||
for (int i = 1; i < Events.Count; i++)
|
||||
{
|
||||
DateTime currentTransaction = DateTime.Parse(Events[i].EVENT_DTM);
|
||||
|
||||
if (currentTransaction.Date == compareDate)
|
||||
{
|
||||
if (currentModeIsUp)
|
||||
uptime += (DateTime.Parse(Events[i].EVENT_DTM) - previousTransaction).TotalMinutes;
|
||||
|
||||
currentModeIsUp = EventIsProduction(Events[i].REACT_MODE);
|
||||
previousTransaction = DateTime.Parse(Events[i].EVENT_DTM);
|
||||
}
|
||||
|
||||
if ((currentTransaction.Date - compareDate).TotalHours == 24)
|
||||
{
|
||||
if (currentModeIsUp)
|
||||
uptime += (currentTransaction.Date - previousTransaction).TotalMinutes;
|
||||
|
||||
data.Add(new ToolUptimeData
|
||||
{
|
||||
Date = compareDate.ToString(),
|
||||
UptimePercentage = uptime / 1440
|
||||
});
|
||||
|
||||
if (currentModeIsUp)
|
||||
uptime = (currentTransaction - currentTransaction.Date).TotalMinutes;
|
||||
else
|
||||
uptime = 0;
|
||||
|
||||
compareDate = compareDate.AddDays(1);
|
||||
currentModeIsUp = EventIsProduction(Events[i].REACT_MODE);
|
||||
previousTransaction = DateTime.Parse(Events[i].EVENT_DTM);
|
||||
}
|
||||
|
||||
if ((currentTransaction.Date - compareDate).TotalHours > 24)
|
||||
{
|
||||
while (currentTransaction.Date != compareDate)
|
||||
{
|
||||
if (currentModeIsUp)
|
||||
uptime += (compareDate.AddDays(1) - previousTransaction).TotalMinutes;
|
||||
|
||||
data.Add(new ToolUptimeData
|
||||
{
|
||||
Date = compareDate.ToString(),
|
||||
UptimePercentage = uptime / 1440
|
||||
});
|
||||
|
||||
uptime = 0;
|
||||
|
||||
compareDate = compareDate.AddDays(1);
|
||||
previousTransaction = compareDate;
|
||||
}
|
||||
}
|
||||
|
||||
if (i == Events.Count - 1)
|
||||
{
|
||||
if ((DateTime.Parse(EndDate).Date != compareDate))
|
||||
{
|
||||
while (DateTime.Parse(EndDate).Date != compareDate)
|
||||
{
|
||||
if (currentModeIsUp)
|
||||
uptime += (compareDate.AddDays(1) - previousTransaction).TotalMinutes;
|
||||
|
||||
data.Add(new ToolUptimeData
|
||||
{
|
||||
Date = compareDate.ToString(),
|
||||
UptimePercentage = uptime / 1440
|
||||
});
|
||||
|
||||
uptime = 0;
|
||||
|
||||
compareDate = compareDate.AddDays(1);
|
||||
previousTransaction = compareDate;
|
||||
}
|
||||
}
|
||||
|
||||
if (currentModeIsUp)
|
||||
uptime += (DateTime.Parse(EndDate) - previousTransaction).TotalMinutes;
|
||||
|
||||
data.Add(new ToolUptimeData
|
||||
{
|
||||
Date = DateTime.Now.Date.ToString(),
|
||||
UptimePercentage = uptime / (DateTime.Parse(EndDate) - DateTime.Parse(EndDate).Date).TotalMinutes
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
private List<ToolUptimeData> LoadUptimeData(bool currentModeIsUp)
|
||||
{
|
||||
List<ToolUptimeData> data = new();
|
||||
|
||||
double days = (DateTime.Parse(EndDate) - DateTime.Parse(StartDate)).TotalDays;
|
||||
|
||||
for (int i = 0; i < days; i++)
|
||||
{
|
||||
data.Add(new ToolUptimeData
|
||||
{
|
||||
Date = DateTime.Parse(StartDate).Date.AddDays(i).ToString(),
|
||||
UptimePercentage = currentModeIsUp ? 1 : 0
|
||||
});
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
private bool EventIsProduction(string evnt)
|
||||
{
|
||||
if (evnt.ToUpper() == "UP")
|
||||
return true;
|
||||
|
||||
if (evnt.ToUpper() == "UP_WITH_INCREASED_SAMPLING")
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user