Files
AiQ_GUI/GoogleAPI.cs
2025-09-02 15:32:24 +01:00

359 lines
16 KiB
C#

using Google.Apis.Auth.OAuth2;
using Google.Apis.Gmail.v1;
using Google.Apis.Services;
using Google.Apis.Sheets.v4;
using Google.Apis.Sheets.v4.Data;
using Google.Apis.Util.Store;
using System.Net.Mail;
using System.Net.Mime;
using System.Reflection;
namespace AiQ_GUI
{
internal class GoogleAPI
{
public static UserCredential credential;
public static SheetsService service = new();
const string ApplicationName = "Google Sheets API .NET Quickstart";
static readonly string credPath = Environment.GetFolderPath(Environment.SpecialFolder.Personal);
public const string spreadsheetId_ModelInfo = "1bCcCr4OYqfjmydt6UqtmN4FQETezXmZRSStJdCCcqZM";
public const string DrivePath = @"G:\Shared drives\MAV Production GUI's\"; // Path to google shared drive
// Startup and make necessary connections to Google servers and make sure user is logged in
public static bool Setup()
{
try
{
string streamPath = $"{DrivePath}R50IQ\\client_secret.json";
string[] Scopes = [SheetsService.Scope.Spreadsheets];
FileStream stream = new(streamPath, FileMode.Open, FileAccess.Read);
credential = GoogleWebAuthorizationBroker.AuthorizeAsync(GoogleClientSecrets.FromStream(stream).Secrets, Scopes, "user", CancellationToken.None, new FileDataStore(credPath, true)).Result;
service = new SheetsService(new BaseClientService.Initializer()
{
HttpClientInitializer = credential,
ApplicationName = ApplicationName,
});
return true;
}
catch
{
return false;
}
}
// Write a 1D array range to a spreadsheet
public static void WriteToSS(List<object> ToWrite, string Range, string SSID)
{
ValueRange ValueRange = new() { Values = [ToWrite] };
SpreadsheetsResource.ValuesResource.UpdateRequest Update = service.Spreadsheets.Values.Update(ValueRange, SSID, Range);
Update.ValueInputOption = SpreadsheetsResource.ValuesResource.UpdateRequest.ValueInputOptionEnum.RAW;
Update.Execute();
}
// Read a range from a spreadsheet, always returns a 2D object even if 1D is requested
public static IList<IList<object>> ReadSS(string sheet, string Location)
{
return service.Spreadsheets.Values.Get(sheet, Location).Execute().Values;
}
// Checks the WIP columns of a given serial number
public static bool CheckWIP(string SerialNumber, string spreadsheetId)
{
int Row = CheckSerialNumRow(spreadsheetId, SerialNumber); // Get row of serial number - 1 because don't want next row in this case
IList<IList<object>> valuesRow = ReadSS(spreadsheetId, $"S{Row}"); // Check if that row is WIP or not
return Convert.ToBoolean(valuesRow[0][0]);
}
// Update serial number register with relevant details about the camera that has passed
public static string UpdateSpreadSheetPreTest(string spreadsheetId, Versions Vers, string CamDesc, string ModelOnTest)
{
try
{
// Finds next row to be used by length of returned array and add 1
IList<IList<object>> values = service.Spreadsheets.Values.Get(spreadsheetId, "B1:B").Execute().Values;
int nextRow = values != null ? values.Count + 1 : 0;
if (values?.Count > 0)
{
// Serial number location = nextRow - 2 (Array starts at 0 but spreadsheet starts at 1 & next row has already added one so need to get rid)
string lastSerialNumber = Convert.ToString(values[nextRow - 2][0]);
int NewSerialNumberInt = Convert.ToInt32(lastSerialNumber.Substring(1)) + 1; // Generate new serial number knowing the last
string newSerialNumber = "K" + NewSerialNumberInt.ToString();
// Send data to spreadsheet and set WIP to TRUE
List<object> oblistAE = // Write columns A-E
[
ModelOnTest,
newSerialNumber,
CamDesc,
"Pre Test: " + DateTime.Now.ToString("dd/MM/yyyy"),
Vers.version + " - " + Vers.revision + Environment.NewLine + Vers.buildtime + Environment.NewLine + Vers.proquint,
];
WriteToSS(oblistAE, $"A{nextRow}:E{nextRow}", spreadsheetId);
// Write MAC to column H
List<object> oblistH = [Vers.MAC];
WriteToSS(oblistH, $"H{nextRow}", spreadsheetId);
List<object> oblistN = [$"GUI Version: {GUIUpdate.GUIVerShort}"]; // Write column N
WriteToSS(oblistN, $"N{nextRow}", spreadsheetId);
// Write TRUE to WIP checkbox in column S
List<object> oblistS = ["TRUE"];
WriteToSS(oblistS, $"S{nextRow}", spreadsheetId);
return newSerialNumber;
}
else
{
return "Last serial number not found";
}
}
catch (Exception ex)
{
return $"ERROR: {ex.Message}";
}
}
// Update serial number register with relevant details about the camera that has passed
public static string UpdateSpreadSheetRePreTest(string spreadsheetId, Versions Vers)
{
try
{
int CamRow = CheckSerialNumRow(spreadsheetId, Vers.Serial);
if (CamRow != 0)
{
// Send data to spreadsheet and set WIP to TRUE
List<object> oblistDE = // Write columns D-E
[
"Pre Test: " + DateTime.Now.ToString("dd/MM/yyyy"),
Vers.version + " - " + Vers.revision + Environment.NewLine + Vers.buildtime + Environment.NewLine + Vers.proquint,
];
WriteToSS(oblistDE, $"D{CamRow}:E{CamRow}", spreadsheetId);
// Write MAC to column H
List<object> oblistH = [Vers.MAC];
WriteToSS(oblistH, $"H{CamRow}", spreadsheetId);
List<object> oblistN = [$"GUI Version: {GUIUpdate.GUIVerShort}"]; // Write column N
WriteToSS(oblistN, $"N{CamRow}", spreadsheetId);
// Write TRUE to WIP checkbox in column S
List<object> oblistS = ["TRUE"];
WriteToSS(oblistS, $"S{CamRow}", spreadsheetId);
return "OK";
}
else
{
return "Serial number not found";
}
}
catch (Exception ex)
{
return $"ERROR: {ex.Message}";
}
}
// Update serial number register with relevant details about the camera that has passed
public static string UpdateSpreadSheetFinalTest(string spreadsheetId, Diags DiagsAPI, SSHData sshData, int RMANum)
{
try
{
int CamRow = CheckSerialNumRow(spreadsheetId, DiagsAPI.serialNumber);
IList<IList<object>> valuesRow = ReadSS(spreadsheetId, $"D{CamRow}");
string TestDate = Convert.ToString(valuesRow[0][0]) + Environment.NewLine + "Final Test: " + DateTime.Now.ToString("dd/MM/yyyy");
if (RMANum != 0)
TestDate = TestDate.Replace("Final", "RMA"); // So it will say RMA Test in the spreadsheet.
// Write column D
List<object> oblistD = [TestDate];
WriteToSS(oblistD, $"D{CamRow}", spreadsheetId);
List<object> oblistFG = // Write columns F-G
[
DiagsAPI.licenses.raptorKeyID,
sshData.packages
];
WriteToSS(oblistFG, $"F{CamRow}:G{CamRow}", spreadsheetId);
List<object> oblistNR = // Write columns N-S
[
$"GUI Version: {GUIUpdate.GUIVerShort}",
DiagsAPI.licenses.saf1,
DiagsAPI.licenses.audit,
DiagsAPI.licenses.stream,
sshData.tailscale,
"FALSE" // Write FALSE to WIP checkbox in column S
];
WriteToSS(oblistNR, $"N{CamRow}:S{CamRow}", spreadsheetId);
return string.Empty;
}
catch (Exception ex)
{
return "Failed to update spreadsheet data, please check manually" + ex.Message;
}
}
// Update Vaxtor spreadsheet
public static string UpdateSpreadSheetVaxtor(VaxtorLic VaxtorLicResp, string serial, string model)
{
try
{
string spreadsheetId = "1n5zhmI4Tz6JFr0stLNFOR6GsxGEKBEhrVKQ6yncM-LA";
int nextRow = CheckNextFree(spreadsheetId);
List<object> oblistCF = // Write columns C-F
[
model,
serial,
DateTime.Now.ToString("dd/MM/yyyy"),
VaxtorLicResp.protectionKeyId,
];
WriteToSS(oblistCF, $"C{nextRow}:F{nextRow}", spreadsheetId);
// Write PROD to column H
List<object> oblistH = ["PROD"];
WriteToSS(oblistH, $"H{nextRow}", spreadsheetId);
return string.Empty;
}
catch (Exception ex)
{
return "Failed to update spreadsheet data, please check manually" + ex.Message;
}
}
// Checks RMA control sheet for a model and serial that match current camera
public static int CheckRMANum(string serial, string model)
{
string spreadsheetId_RMAControl = "1tZhkYrqBQ3BcL7ZS4q3ghzCgHSJ8f5LVSj7nh6fIRC8";
try
{
// Get all info in H and I columns
IList<IList<object>> valuesRMA = service.Spreadsheets.Values.Get(spreadsheetId_RMAControl, "H2:I").Execute().Values;
for (int i = 0; i < valuesRMA.Count; i++)
{
try // In case line is blank
{
// Checks is serial and model num in RMA control spreadsheet match what is in the camera
if (valuesRMA[i][0].ToString().Contains(serial) && valuesRMA[i][1].ToString().Contains(model))
{
int OnRow = i + 2; // Offset for start of the sheet and starting at 1
valuesRMA = service.Spreadsheets.Values.Get(spreadsheetId_RMAControl, "A" + OnRow).Execute().Values;
return Convert.ToInt16(valuesRMA[0][0]); // Once it has found the serial it gusses at the RMA number
}
}
catch { }
}
}
catch { }
return 0; // If it can't be found
}
// Checks RMA control sheet for a model and serial that match current camera
public static int CheckSerialNumRow(string spreadsheetId, string serial)
{
// Get all info in B column
IList<IList<object>> valuesRMA = service.Spreadsheets.Values.Get(spreadsheetId, "B:B").Execute().Values;
for (int i = 0; i < valuesRMA.Count; i++)
{
try // In case line is blank
{
// Checks is serial and model num in RMA control spreadsheet match what is in the camera
if (valuesRMA[i][0].ToString().Contains(serial))
{
return i + 1; // Offset for sheet starting at 1
}
}
catch { }
}
return 0; // If it can't be found
}
// Checks Vaxtor sheet for next free row
public static int CheckNextFree(string spreadsheetId)
{
IList<IList<object>> valuesRMA = service.Spreadsheets.Values.Get(spreadsheetId, "C2:C").Execute().Values; // Get how many C cows
return valuesRMA.Count + 2; // Gets the amount of rows, offsets for start from 1 error and adds one to be next row
}
public static void EmailApproval(string ApprovalRow, string User)
{
FileStream GmailStream = new($"{DrivePath}R50IQ\\creds.json", FileMode.Open, FileAccess.Read);
string[] ScopesGmail = [GmailService.Scope.GmailSend];
using (GmailStream)
{
string credPathGmail = Path.Combine(credPath, ".credentials/gmail-dotnet-quickstart.json");
credential = GoogleWebAuthorizationBroker.AuthorizeAsync(GoogleClientSecrets.FromStream(GmailStream).Secrets, ScopesGmail, "user", CancellationToken.None, new FileDataStore(credPathGmail, true)).Result;
GmailStream.Close();
}
// Build the MIME message with attachment
using MailMessage mail = new MailMessage();
mail.From = new MailAddress("me");
mail.To.Add("richard.porter@mav-systems.com");
mail.To.Add("bradley.relyea@mav-systems.com");
mail.To.Add("bradley.born@mav-systems.com");
mail.Subject = "Approval required";
mail.Body = $"Dear Rich,<br><br>Camera needs approval<br>" +
$"https://docs.google.com/spreadsheets/d/1bCcCr4OYqfjmydt6UqtmN4FQETezXmZRSStJdCCcqZM/edit#gid=1931079354&range=A{ApprovalRow}" +
$"<br><br><br>Thanks,<br><br>{User}";
mail.IsBodyHtml = true;
// Attach the log file if it exists
string logFilePath = LDS.MAVPath + Logging.LogFileName;
if (File.Exists(logFilePath))
{
Attachment logAttachment = new(logFilePath, MediaTypeNames.Text.Plain);
logAttachment.Name = Logging.LogFileName;
mail.Attachments.Add(logAttachment);
}
// Save the MIME message to a stream
using MemoryStream ms = new();
SmtpClient smtpClient = new(); // Only used to access the internal Write method
Type mailWriterType = typeof(SmtpClient).Assembly.GetType("System.Net.Mail.MailWriter");
object? mailWriter = Activator.CreateInstance(
mailWriterType,
BindingFlags.Instance | BindingFlags.NonPublic,
null,
[ms, true],
null);
typeof(MailMessage).InvokeMember(
"Send",
BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.InvokeMethod,
null,
mail,
[mailWriter, true, true]);
ms.Position = 0;
byte[] rawBytes = ms.ToArray();
GmailService service = new(new BaseClientService.Initializer()
{
HttpClientInitializer = credential,
ApplicationName = ApplicationName,
});
Google.Apis.Gmail.v1.Data.Message newMsg = new()
{
Raw = Convert.ToBase64String(rawBytes)
.Replace("+", "-")
.Replace("/", "_")
.Replace("=", "")
};
service.Users.Messages.Send(newMsg, "me").Execute();
}
}
}