1+ using System ;
2+ using System . Collections . Generic ;
3+ using System . Linq ;
4+ using System . Threading . Tasks ;
5+ using Google . Apis . Auth . OAuth2 ;
6+ using Google . Apis . Services ;
7+ using Google . Apis . Sheets . v4 ;
8+ using Google . Apis . Sheets . v4 . Data ;
9+ using Microsoft . EntityFrameworkCore ;
10+ using Microsoft . Extensions . Logging ;
11+ using Microting . eForm . Infrastructure . Constants ;
12+ using Microting . eFormApi . BasePn . Abstractions ;
13+ using Microting . eFormApi . BasePn . Infrastructure . Helpers . PluginDbOptions ;
14+ using Microting . TimePlanningBase . Infrastructure . Data ;
15+ using TimePlanning . Pn . Infrastructure . Models . Settings ;
16+
17+ namespace TimePlanning . Pn . Infrastructure . Helpers ;
18+
19+ public class GoogleSheetHelper
20+ {
21+ public static async Task PushToGoogleSheet ( IPluginDbOptions < TimePlanningBaseSettings > options ,
22+ IEFormCoreService coreHelper , TimePlanningPnDbContext dbContext , ILogger logger )
23+ {
24+ var privateKeyId = Environment . GetEnvironmentVariable ( "PRIVATE_KEY_ID" ) ;
25+ if ( string . IsNullOrEmpty ( privateKeyId ) )
26+ {
27+ return ;
28+ }
29+
30+ var applicationName = "Google Sheets API Integration" ;
31+ var spreadsheetId = options . Value . GoogleSheetId ;
32+ var sheetName = "PlanTimer" ;
33+
34+ var core = await coreHelper . GetCore ( ) ;
35+ await using var sdkDbContext = core . DbContextHelper . GetDbContext ( ) ;
36+
37+ var privateKey = Environment . GetEnvironmentVariable ( "PRIVATE_KEY" ) ; // Replace with your private key
38+ var clientEmail = Environment . GetEnvironmentVariable ( "CLIENT_EMAIL" ) ; // Replace with your client email
39+ var projectId = Environment . GetEnvironmentVariable ( "PROJECT_ID" ) ; // Replace with your project ID
40+ var clientId = Environment . GetEnvironmentVariable ( "CLIENT_ID" ) ; // Replace with your client ID
41+
42+ // Construct the JSON for the service account credentials
43+ string serviceAccountJson = $@ "
44+ {{
45+ ""type"": ""service_account"",
46+ ""project_id"": ""{ projectId } "",
47+ ""private_key_id"": ""{ privateKeyId } "",
48+ ""private_key"": ""{ privateKey } "",
49+ ""client_email"": ""{ clientEmail } "",
50+ ""client_id"": ""{ clientId } "",
51+ ""auth_uri"": ""https://accounts.google.com/o/oauth2/auth"",
52+ ""token_uri"": ""https://oauth2.googleapis.com/token"",
53+ ""auth_provider_x509_cert_url"": ""https://www.googleapis.com/oauth2/v1/certs"",
54+ ""client_x509_cert_url"": ""https://www.googleapis.com/robot/v1/metadata/x509/{ clientEmail } ""
55+ }}" ;
56+
57+ // Authenticate using the dynamically constructed JSON
58+ var credential = GoogleCredential . FromJson ( serviceAccountJson )
59+ . CreateScoped ( SheetsService . Scope . Spreadsheets ) ;
60+
61+ var service = new SheetsService ( new BaseClientService . Initializer
62+ {
63+ HttpClientInitializer = credential ,
64+ ApplicationName = applicationName
65+ } ) ;
66+
67+ try
68+ {
69+ var headerRequest = service . Spreadsheets . Values . Get ( spreadsheetId , $ "{ sheetName } !A1:1") ;
70+ var headerResponse = await headerRequest . ExecuteAsync ( ) ;
71+ var existingHeaders = headerResponse . Values ? . FirstOrDefault ( ) ?? new List < object > ( ) ;
72+
73+ var assignedSites = await dbContext . AssignedSites
74+ . Where ( x => x . WorkflowState != Constants . WorkflowStates . Removed )
75+ . Select ( x => x . SiteId )
76+ . Distinct ( )
77+ . ToListAsync ( ) ;
78+
79+ var siteNames = await sdkDbContext . Sites
80+ . Where ( x => assignedSites . Contains ( x . MicrotingUid ! . Value ) )
81+ . OrderBy ( x => x . Name )
82+ . Select ( x => x . Name )
83+ . ToListAsync ( ) ;
84+
85+ var newHeaders = existingHeaders . Cast < string > ( ) . ToList ( ) ;
86+ foreach ( var siteName in siteNames )
87+ {
88+ var timerHeader = $ "{ siteName } - timer";
89+ var textHeader = $ "{ siteName } - tekst";
90+ if ( ! newHeaders . Contains ( timerHeader ) )
91+ {
92+ newHeaders . Add ( timerHeader ) ;
93+ }
94+
95+ if ( ! newHeaders . Contains ( textHeader ) )
96+ {
97+ newHeaders . Add ( textHeader ) ;
98+ }
99+ }
100+
101+ if ( ! existingHeaders . Cast < string > ( ) . SequenceEqual ( newHeaders ) )
102+ {
103+ var updateRequest = new ValueRange
104+ {
105+ Values = new List < IList < object > > { newHeaders . Cast < object > ( ) . ToList ( ) }
106+ } ;
107+
108+ var columnLetter = GetColumnLetter ( newHeaders . Count ) ;
109+ updateRequest = new ValueRange
110+ {
111+ Values = new List < IList < object > > { newHeaders . Cast < object > ( ) . ToList ( ) }
112+ } ;
113+ var updateHeaderRequest =
114+ service . Spreadsheets . Values . Update ( updateRequest , spreadsheetId , $ "{ sheetName } !A1:{ columnLetter } 1") ;
115+ updateHeaderRequest . ValueInputOption =
116+ SpreadsheetsResource . ValuesResource . UpdateRequest . ValueInputOptionEnum . RAW ;
117+ await updateHeaderRequest . ExecuteAsync ( ) ;
118+
119+ logger . LogInformation ( "Headers updated successfully." ) ;
120+ }
121+
122+ AutoAdjustColumnWidths ( service , spreadsheetId , sheetName , logger ) ;
123+
124+ SetAlternatingColumnColors ( service , spreadsheetId , 0 , newHeaders . Count , logger ) ;
125+
126+ logger . LogInformation ( "Headers are already up-to-date." ) ;
127+ }
128+ catch ( Exception ex )
129+ {
130+ logger . LogError ( $ "An error occurred: { ex . Message } ") ;
131+ }
132+ }
133+
134+ static void AutoAdjustColumnWidths ( SheetsService service , string spreadsheetId , string sheetName , ILogger logger )
135+ {
136+ try
137+ {
138+ var sheet = service . Spreadsheets . Get ( spreadsheetId ) . Execute ( ) . Sheets
139+ . FirstOrDefault ( s => s . Properties . Title == sheetName ) ;
140+ if ( sheet == null ) throw new Exception ( $ "Sheet '{ sheetName } ' not found.") ;
141+
142+ var sheetId = sheet . Properties . SheetId ;
143+
144+ var autoResizeRequest = new Request
145+ {
146+ AutoResizeDimensions = new AutoResizeDimensionsRequest
147+ {
148+ Dimensions = new DimensionRange
149+ {
150+ SheetId = sheetId ,
151+ Dimension = "COLUMNS" ,
152+ StartIndex = 0 , // Start from the first column
153+ EndIndex = sheet . Properties . GridProperties . ColumnCount // Auto-adjust all columns
154+ }
155+ }
156+ } ;
157+
158+ var batchRequest = new BatchUpdateSpreadsheetRequest
159+ {
160+ Requests = new List < Request > { autoResizeRequest }
161+ } ;
162+
163+ service . Spreadsheets . BatchUpdate ( batchRequest , spreadsheetId ) . Execute ( ) ;
164+
165+ logger . LogInformation ( "Column widths auto-adjusted successfully." ) ;
166+ }
167+ catch ( Exception ex )
168+ {
169+ logger . LogError ( $ "An error occurred while auto-adjusting column widths: { ex . Message } ") ;
170+ }
171+ }
172+
173+ private static string GetColumnLetter ( int columnIndex )
174+ {
175+ string columnLetter = "" ;
176+ while ( columnIndex > 0 )
177+ {
178+ int modulo = ( columnIndex - 1 ) % 26 ;
179+ columnLetter = Convert . ToChar ( 65 + modulo ) + columnLetter ;
180+ columnIndex = ( columnIndex - modulo ) / 26 ;
181+ }
182+
183+ return columnLetter ;
184+ }
185+
186+ static void SetAlternatingColumnColors ( SheetsService service , string spreadsheetId , int sheetId , int columnCount , ILogger logger )
187+ {
188+ var requests = new List < Request > ( ) ;
189+
190+ for ( int i = 3 ; i < columnCount ; i ++ ) // Start from column D (index 3)
191+ {
192+ var color = ( i % 2 == 0 )
193+ ? new Color { Red = 1 , Green = 1 , Blue = 1 }
194+ : new Color { Red = 0.9f , Green = 0.9f , Blue = 0.9f } ;
195+
196+ var updateCellsRequest = new Request
197+ {
198+ RepeatCell = new RepeatCellRequest
199+ {
200+ Range = new GridRange
201+ {
202+ SheetId = sheetId ,
203+ StartColumnIndex = i ,
204+ EndColumnIndex = i + 1
205+ } ,
206+ Cell = new CellData
207+ {
208+ UserEnteredFormat = new CellFormat
209+ {
210+ BackgroundColor = color
211+ }
212+ } ,
213+ Fields = "userEnteredFormat.backgroundColor"
214+ }
215+ } ;
216+
217+ requests . Add ( updateCellsRequest ) ;
218+ }
219+
220+ var batchUpdateRequest = new BatchUpdateSpreadsheetRequest
221+ {
222+ Requests = requests
223+ } ;
224+
225+ service . Spreadsheets . BatchUpdate ( batchUpdateRequest , spreadsheetId ) . Execute ( ) ;
226+
227+ logger . LogInformation ( "Alternating column colors set successfully." ) ;
228+ }
229+ }
0 commit comments