entropy25 commited on
Commit
b8f5958
Β·
verified Β·
1 Parent(s): 58070eb

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +363 -404
app.py CHANGED
@@ -4,445 +4,404 @@ import numpy as np
4
  import plotly.express as px
5
  import plotly.graph_objects as go
6
  from datetime import datetime, timedelta
7
- import random
8
  from io import StringIO, BytesIO
9
  import base64
10
  import json
 
 
 
11
 
12
- # Page config
13
- st.set_page_config(
14
- page_title="ESG Compliance Intelligence",
15
- page_icon="🌱",
16
- layout="wide",
17
- initial_sidebar_state="expanded"
18
- )
 
 
19
 
20
- # Enhanced CSS styling matching React design
21
- st.markdown("""
22
- <style>
23
- .main-header {
24
- background: linear-gradient(135deg, #1e40af 0%, #059669 100%);
25
- padding: 2rem;
26
- border-radius: 15px;
27
- margin-bottom: 2rem;
28
- color: white;
29
- text-align: center;
30
- box-shadow: 0 8px 32px rgba(0,0,0,0.1);
31
- }
32
- .metric-card {
33
- background: white;
34
- padding: 1.5rem;
35
- border-radius: 12px;
36
- border: 2px solid #e5e7eb;
37
- box-shadow: 0 4px 20px rgba(0,0,0,0.08);
38
- text-align: center;
39
- height: 120px;
40
- transition: transform 0.2s ease;
41
- }
42
- .metric-card:hover {
43
- transform: translateY(-2px);
44
- }
45
- .alert-danger {
46
- background: linear-gradient(135deg, #fef2f2 0%, #fee2e2 100%);
47
- border-left: 4px solid #dc2626;
48
- padding: 1rem;
49
- border-radius: 8px;
50
- margin: 1rem 0;
51
- box-shadow: 0 2px 8px rgba(220, 38, 38, 0.1);
52
- }
53
- .alert-success {
54
- background: linear-gradient(135deg, #f0fdf4 0%, #dcfce7 100%);
55
- border-left: 4px solid #059669;
56
- padding: 1rem;
57
- border-radius: 8px;
58
- margin: 1rem 0;
59
- box-shadow: 0 2px 8px rgba(5, 150, 105, 0.1);
60
- }
61
- .alert-warning {
62
- background: linear-gradient(135deg, #fffbeb 0%, #fef3c7 100%);
63
- border-left: 4px solid #f59e0b;
64
- padding: 1rem;
65
- border-radius: 8px;
66
- margin: 1rem 0;
67
- box-shadow: 0 2px 8px rgba(245, 158, 11, 0.1);
68
- }
69
- .chart-container {
70
- background: white;
71
- padding: 1.5rem;
72
- border-radius: 12px;
73
- box-shadow: 0 4px 20px rgba(0,0,0,0.08);
74
- margin: 1rem 0;
75
- border: 1px solid #f1f5f9;
76
- }
77
- .footer-info {
78
- text-align: center;
79
- color: #64748b;
80
- font-size: 0.9rem;
81
- margin-top: 2rem;
82
- padding: 1.5rem;
83
- background: linear-gradient(135deg, #f8fafc 0%, #f1f5f9 100%);
84
- border-radius: 12px;
85
- border: 1px solid #e2e8f0;
86
- }
87
- .download-btn {
88
- background: linear-gradient(135deg, #3b82f6 0%, #1d4ed8 100%);
89
- border: none;
90
- color: white;
91
- padding: 0.75rem 1.5rem;
92
- border-radius: 8px;
93
- cursor: pointer;
94
- font-weight: 500;
95
- transition: all 0.2s ease;
96
- }
97
- .download-btn:hover {
98
- transform: translateY(-1px);
99
- box-shadow: 0 4px 12px rgba(59, 130, 246, 0.3);
100
- }
101
- </style>
102
- """, unsafe_allow_html=True)
103
 
104
- # Sample CSV data for demo
105
- def get_sample_data():
106
- return """timestamp,ph_level,wastewater_lmin,co2_emission_kg,energy_kwh,chemical_usage_kg,location,status
 
 
107
  2024-08-27 14:30,7.4,48.5,14.2,92,2.3,Statoil Platform Alpha,compliant
108
  2024-08-27 14:25,7.2,45.1,12.8,88,2.1,Statoil Platform Alpha,compliant
109
  2024-08-27 14:20,7.6,52.3,15.7,95,2.5,Statoil Platform Alpha,warning
110
  2024-08-27 14:15,7.8,55.2,16.1,98,2.8,Statoil Platform Alpha,violation
111
- 2024-08-27 14:10,7.3,47.8,13.5,89,2.2,Statoil Platform Alpha,compliant
112
- 2024-08-27 14:05,7.1,44.2,12.1,85,2.0,Statoil Platform Alpha,compliant"""
113
-
114
- # Load data function
115
- @st.cache_data
116
- def load_data(uploaded_file):
117
- if uploaded_file:
118
- df = pd.read_csv(uploaded_file)
119
- else:
120
- df = pd.read_csv(StringIO(get_sample_data()))
121
 
122
- df['timestamp'] = pd.to_datetime(df['timestamp'])
123
- return df
124
-
125
- # Carbon footprint data
126
- def get_carbon_data():
127
- return pd.DataFrame({
128
- 'source': ['Fuel Consumption', 'Electricity', 'Chemicals', 'Transport'],
129
- 'co2_kg': [145.2, 67.8, 89.1, 19.6],
130
- 'percentage': [45, 21, 28, 6]
131
- })
132
-
133
- # Generate PSA report with export functionality
134
- def generate_psa_report(df):
135
- latest_data = df.iloc[-1]
136
- report = {
137
- 'report_id': f"PSA-ENV-{datetime.now().strftime('%Y%m%d%H%M')}",
138
- 'timestamp': datetime.now().isoformat(),
139
- 'location': latest_data['location'],
140
- 'ph_level': float(latest_data['ph_level']),
141
- 'wastewater_volume': float(latest_data['wastewater_lmin']),
142
- 'co2_total': float(latest_data['co2_emission_kg']),
143
- 'energy_consumption': float(latest_data['energy_kwh']),
144
- 'compliance_status': latest_data['status'].upper(),
145
- 'generated_by': 'ESG Compliance Intelligence Engine',
146
- 'certification': 'PSA-Compliant Format'
147
- }
148
- return report
149
 
150
- # Create downloadable PDF content
151
- def create_pdf_report(report_data, df):
152
- html_content = f"""
153
- <!DOCTYPE html>
154
- <html>
155
- <head>
156
- <meta charset="UTF-8">
157
- <title>PSA Environmental Compliance Report</title>
158
  <style>
159
- body {{ font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; margin: 40px; }}
160
- .header {{ background: linear-gradient(135deg, #1e40af 0%, #059669 100%);
161
- color: white; padding: 20px; border-radius: 8px; text-align: center; }}
162
- .section {{ margin: 20px 0; padding: 15px; border: 1px solid #ddd; border-radius: 8px; }}
163
- .compliant {{ color: #059669; font-weight: bold; }}
164
- .warning {{ color: #f59e0b; font-weight: bold; }}
165
- .violation {{ color: #dc2626; font-weight: bold; }}
166
- .data-table {{ width: 100%; border-collapse: collapse; margin: 10px 0; }}
167
- .data-table th, .data-table td {{ border: 1px solid #ddd; padding: 8px; text-align: left; }}
168
- .data-table th {{ background-color: #f8fafc; }}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
169
  </style>
170
- </head>
171
- <body>
172
- <div class="header">
173
- <h1>Norwegian Petroleum Safety Authority</h1>
174
- <h2>Environmental Compliance Report</h2>
175
- <p>Report ID: {report_data['report_id']}</p>
176
- </div>
177
-
178
- <div class="section">
179
- <h3>Report Summary</h3>
180
- <p><strong>Generated:</strong> {report_data['timestamp']}</p>
181
- <p><strong>Location:</strong> {report_data['location']}</p>
182
- <p><strong>Compliance Status:</strong>
183
- <span class="{report_data['compliance_status'].lower()}">{report_data['compliance_status']}</span>
184
- </p>
185
- </div>
186
-
187
- <div class="section">
188
- <h3>Environmental Measurements</h3>
189
- <table class="data-table">
190
- <tr><th>Parameter</th><th>Value</th><th>Unit</th><th>PSA Limit</th><th>Status</th></tr>
191
- <tr><td>pH Level</td><td>{report_data['ph_level']:.1f}</td><td>pH</td><td>6.0-8.5</td><td>βœ“ Within Range</td></tr>
192
- <tr><td>Wastewater Volume</td><td>{report_data['wastewater_volume']:.1f}</td><td>L/min</td><td>&lt;60</td><td>βœ“ Within Range</td></tr>
193
- <tr><td>COβ‚‚ Emissions</td><td>{report_data['co2_total']:.1f}</td><td>kg</td><td>Monitor</td><td>Tracked</td></tr>
194
- <tr><td>Energy Consumption</td><td>{report_data['energy_consumption']:.1f}</td><td>kWh</td><td>Monitor</td><td>Tracked</td></tr>
195
- </table>
196
  </div>
 
 
 
 
 
 
 
 
 
197
 
198
- <div class="section">
199
- <h3>Certification</h3>
200
- <p>This report has been generated automatically by the ESG Compliance Intelligence Engine
201
- in accordance with Norwegian Petroleum Safety Authority requirements.</p>
202
- <p><strong>Digital Signature:</strong> Verified βœ“</p>
203
- <p><strong>Report Hash:</strong> {hash(str(report_data)) % 1000000:06d}</p>
204
  </div>
205
- </body>
206
- </html>
207
- """
208
- return html_content
209
 
210
- # Create Excel export
211
- def create_excel_report(df, carbon_df):
212
- output = BytesIO()
213
- with pd.ExcelWriter(output, engine='openpyxl') as writer:
214
- df.to_excel(writer, sheet_name='Environmental_Data', index=False)
215
- carbon_df.to_excel(writer, sheet_name='Carbon_Footprint', index=False)
216
-
217
- # Add summary sheet
218
- summary_df = pd.DataFrame({
219
- 'Metric': ['Latest pH', 'Latest COβ‚‚', 'Latest Energy', 'Compliance Status'],
220
- 'Value': [df.iloc[-1]['ph_level'], df.iloc[-1]['co2_emission_kg'],
221
- df.iloc[-1]['energy_kwh'], df.iloc[-1]['status'].upper()]
222
- })
223
- summary_df.to_excel(writer, sheet_name='Summary', index=False)
224
 
225
- return output.getvalue()
226
-
227
- # Download button helper
228
- def get_download_link(file_content, file_name, file_type="application/octet-stream"):
229
- b64_content = base64.b64encode(file_content).decode()
230
- return f'<a href="data:{file_type};base64,{b64_content}" download="{file_name}">Download {file_name}</a>'
231
-
232
- # Main app
233
- def main():
234
- # Header with gradient background
235
- st.markdown("""
236
- <div class="main-header">
237
- <h1>🌱 ESG & Compliance Intelligence Engine</h1>
238
- <p>Norwegian Petroleum Safety Authority (PSA) Real-time Monitoring</p>
239
- </div>
240
- """, unsafe_allow_html=True)
241
 
242
- # Sidebar for data upload and controls
243
- with st.sidebar:
244
- st.header("βš™οΈ Controls")
245
-
246
- uploaded_file = st.file_uploader("Upload CSV Data", type=['csv'])
247
-
248
- if st.button("πŸ”„ Refresh Data"):
249
- st.cache_data.clear()
250
- st.rerun()
251
 
252
- st.markdown("---")
253
-
254
- if st.button("πŸ“Š Generate PSA Report"):
255
- df = load_data(uploaded_file)
256
- report = generate_psa_report(df)
257
- st.success(f"Report Generated: {report['report_id']}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
258
 
259
- # Create downloadable content
260
- pdf_content = create_pdf_report(report, df)
261
- st.download_button(
262
- label="⬇️ Download PDF Report",
263
- data=pdf_content,
264
- file_name=f"PSA_Report_{report['report_id']}.html",
265
- mime="text/html"
266
- )
267
 
268
- st.json(report)
269
-
270
- st.markdown("---")
271
- st.markdown("**Sample Data Format:**")
272
- st.code("""timestamp,ph_level,wastewater_lmin,
273
- co2_emission_kg,energy_kwh,
274
- chemical_usage_kg,location,status""", language="csv")
275
-
276
- # Load data
277
- df = load_data(uploaded_file)
278
- carbon_df = get_carbon_data()
279
-
280
- # Alert system based on latest status
281
- latest_status = df.iloc[-1]['status']
282
- latest_location = df.iloc[-1]['location']
283
-
284
- if latest_status == 'violation':
285
- st.markdown(f"""
286
- <div class="alert-danger">
287
- ⚠️ <strong>CRITICAL ALERT:</strong> Environmental violation detected at {latest_location}!
288
- <br>Immediate action required - PSA notification pending.
289
- </div>
290
- """, unsafe_allow_html=True)
291
- elif latest_status == 'warning':
292
- st.markdown(f"""
293
- <div class="alert-warning">
294
- ⚠️ <strong>WARNING:</strong> Environmental parameters approaching limits at {latest_location}.
295
- <br>Monitor closely and prepare corrective measures.
296
- </div>
297
- """, unsafe_allow_html=True)
298
- else:
299
- st.markdown(f"""
300
- <div class="alert-success">
301
- βœ… <strong>COMPLIANT:</strong> All systems operating within PSA regulations at {latest_location}.
302
- </div>
303
- """, unsafe_allow_html=True)
304
-
305
- # Metrics row
306
- latest_data = df.iloc[-1]
307
-
308
- col1, col2, col3, col4 = st.columns(4)
309
-
310
- with col1:
311
- status_icons = {"compliant": "βœ…", "warning": "⚠️", "violation": "🚨"}
312
- st.metric(
313
- "Compliance Status",
314
- f"{status_icons[latest_status]} {latest_status.upper()}",
315
- delta="Within Limits" if latest_status == "compliant" else "Action Required"
316
- )
317
-
318
- with col2:
319
- ph_delta = latest_data['ph_level'] - 7.0
320
- st.metric(
321
- "Water pH Level",
322
- f"{latest_data['ph_level']:.1f}",
323
- delta=f"{ph_delta:+.1f}"
324
- )
325
 
326
- with col3:
327
- st.metric(
328
- "COβ‚‚ Emissions",
329
- f"{latest_data['co2_emission_kg']:.1f} kg",
330
- delta="-2.3 kg"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
331
  )
 
332
 
333
- with col4:
334
- st.metric(
335
- "Energy Usage",
336
- f"{latest_data['energy_kwh']:.0f} kWh",
337
- delta="+5.2 kWh"
338
- )
 
 
 
 
 
 
 
 
 
339
 
340
- # Charts section
341
- col1, col2 = st.columns(2)
 
 
 
 
 
 
 
 
 
 
 
 
 
342
 
343
- with col1:
344
- st.markdown('<div class="chart-container">', unsafe_allow_html=True)
345
- st.subheader("πŸ’§ Real-time Environmental Monitoring")
 
 
 
 
 
 
 
 
 
 
 
 
 
346
 
347
- fig_line = px.line(df, x='timestamp', y=['ph_level', 'wastewater_lmin'],
348
- title="pH Level & Wastewater Discharge Over Time",
349
- labels={'value': 'Measurement', 'variable': 'Parameter'})
350
- fig_line.update_layout(height=400, showlegend=True)
351
- st.plotly_chart(fig_line, use_container_width=True)
352
  st.markdown('</div>', unsafe_allow_html=True)
353
 
354
- with col2:
355
  st.markdown('<div class="chart-container">', unsafe_allow_html=True)
356
- st.subheader("πŸ“ˆ Carbon Footprint Breakdown")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
357
 
358
- fig_bar = px.bar(carbon_df, x='source', y='co2_kg',
359
- title="COβ‚‚ Emissions by Source",
360
- color='co2_kg',
361
- color_continuous_scale='Greens')
362
- fig_bar.update_layout(height=400)
363
- st.plotly_chart(fig_bar, use_container_width=True)
364
  st.markdown('</div>', unsafe_allow_html=True)
365
 
366
- # Compliance trend
367
- st.markdown('<div class="chart-container">', unsafe_allow_html=True)
368
- st.subheader("πŸ“ˆ Compliance Status Trends")
369
-
370
- # Create compliance score (numeric representation)
371
- compliance_map = {'compliant': 100, 'warning': 70, 'violation': 30}
372
- df['compliance_score'] = df['status'].map(compliance_map)
373
-
374
- fig_area = px.area(df, x='timestamp', y='compliance_score',
375
- title="Compliance Score Over Time",
376
- color_discrete_sequence=['#059669'])
377
- fig_area.add_hline(y=80, line_dash="dash", line_color="red",
378
- annotation_text="PSA Minimum Threshold")
379
- fig_area.update_layout(height=300)
380
- st.plotly_chart(fig_area, use_container_width=True)
381
- st.markdown('</div>', unsafe_allow_html=True)
382
-
383
- # PSA Reports Table
384
- st.markdown('<div class="chart-container">', unsafe_allow_html=True)
385
- st.subheader("πŸ“‹ PSA Compliance Reports History")
386
-
387
- # Sample historical reports
388
- reports_df = pd.DataFrame({
389
- 'Report ID': ['PSA-ENV-2024-001', 'PSA-ENV-2024-002', 'PSA-ENV-2024-003'],
390
- 'Generated': ['2024-08-27 14:30', '2024-08-27 10:15', '2024-08-26 16:45'],
391
- 'Location': ['Statoil Platform Alpha', 'Equinor Platform Beta', 'Aker BP Platform Gamma'],
392
- 'Status': ['βœ… COMPLIANT', '⚠️ WARNING', 'βœ… COMPLIANT'],
393
- 'COβ‚‚ (kg)': [321.7, 345.2, 298.1]
394
- })
395
-
396
- st.dataframe(reports_df, use_container_width=True, hide_index=True)
397
- st.markdown('</div>', unsafe_allow_html=True)
398
-
399
- # Download section
400
- st.markdown('<div class="chart-container">', unsafe_allow_html=True)
401
- st.subheader("⬇️ Export & Reports")
402
-
403
- col1, col2, col3 = st.columns(3)
404
-
405
- with col1:
406
- if st.button("πŸ“„ Generate PDF Report", type="primary"):
407
- report = generate_psa_report(df)
408
- pdf_content = create_pdf_report(report, df)
409
- st.download_button(
410
- label="⬇️ Download PSA Report",
411
- data=pdf_content,
412
- file_name=f"PSA_Report_{datetime.now().strftime('%Y%m%d_%H%M')}.html",
413
- mime="text/html"
414
- )
415
- st.success("βœ… PSA-compliant report ready for download!")
416
-
417
- with col2:
418
- if st.button("πŸ“Š Export Excel Data"):
419
- excel_data = create_excel_report(df, carbon_df)
420
- st.download_button(
421
- label="⬇️ Download Excel File",
422
- data=excel_data,
423
- file_name=f"ESG_Data_{datetime.now().strftime('%Y%m%d_%H%M')}.xlsx",
424
- mime="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
425
- )
426
- st.success("βœ… Excel report with all environmental data exported!")
427
-
428
- with col3:
429
- if st.button("πŸ“€ PSA Portal Integration"):
430
- # Simulate PSA portal submission
431
- submission_id = f"PSA-SUB-{datetime.now().strftime('%Y%m%d%H%M')}"
432
- st.success(f"βœ… Report submitted to PSA Portal!\nSubmission ID: {submission_id}")
433
-
434
- st.markdown('</div>', unsafe_allow_html=True)
435
-
436
- # Footer
437
- st.markdown("""
438
- <div class="footer-info">
439
- πŸ”’ <strong>Security:</strong> AES-256 encryption |
440
- πŸ“Š <strong>Updates:</strong> Real-time data processing |
441
- βœ… <strong>Compliance:</strong> GDPR & Norwegian Data Protection Act
442
- <br><br>
443
- <em>Built for Norwegian R&D grant applications - AI-driven compliance automation</em>
444
- </div>
445
- """, unsafe_allow_html=True)
446
 
447
  if __name__ == "__main__":
448
  main()
 
4
  import plotly.express as px
5
  import plotly.graph_objects as go
6
  from datetime import datetime, timedelta
 
7
  from io import StringIO, BytesIO
8
  import base64
9
  import json
10
+ from dataclasses import dataclass, asdict
11
+ from typing import Optional, Dict, List
12
+ import hashlib
13
 
14
+ # Configuration
15
+ @dataclass
16
+ class Config:
17
+ PAGE_TITLE = "ESG Compliance Intelligence"
18
+ PAGE_ICON = "🌱"
19
+ REQUIRED_COLUMNS = ['timestamp', 'ph_level', 'wastewater_lmin', 'co2_emission_kg', 'energy_kwh', 'location', 'status']
20
+ PH_RANGE = (6.0, 8.5)
21
+ WASTEWATER_LIMIT = 60.0
22
+ COMPLIANCE_THRESHOLD = 80
23
 
24
+ # Data Models
25
+ @dataclass
26
+ class ComplianceReport:
27
+ report_id: str
28
+ timestamp: str
29
+ location: str
30
+ ph_level: float
31
+ wastewater_volume: float
32
+ co2_total: float
33
+ energy_consumption: float
34
+ compliance_status: str
35
+ generated_by: str = "ESG Compliance Engine"
36
+
37
+ @classmethod
38
+ def from_dataframe_row(cls, row):
39
+ return cls(
40
+ report_id=f"PSA-ENV-{datetime.now().strftime('%Y%m%d%H%M')}",
41
+ timestamp=datetime.now().isoformat(),
42
+ location=row['location'],
43
+ ph_level=float(row['ph_level']),
44
+ wastewater_volume=float(row['wastewater_lmin']),
45
+ co2_total=float(row['co2_emission_kg']),
46
+ energy_consumption=float(row['energy_kwh']),
47
+ compliance_status=row['status'].upper()
48
+ )
49
+
50
+ # Data Validation
51
+ class DataValidator:
52
+ @staticmethod
53
+ def validate_csv_structure(df: pd.DataFrame) -> None:
54
+ missing_cols = [col for col in Config.REQUIRED_COLUMNS if col not in df.columns]
55
+ if missing_cols:
56
+ raise ValueError(f"Missing required columns: {', '.join(missing_cols)}")
57
+
58
+ @staticmethod
59
+ def validate_data_types(df: pd.DataFrame) -> pd.DataFrame:
60
+ try:
61
+ df['timestamp'] = pd.to_datetime(df['timestamp'])
62
+ df['ph_level'] = pd.to_numeric(df['ph_level'])
63
+ df['wastewater_lmin'] = pd.to_numeric(df['wastewater_lmin'])
64
+ df['co2_emission_kg'] = pd.to_numeric(df['co2_emission_kg'])
65
+ df['energy_kwh'] = pd.to_numeric(df['energy_kwh'])
66
+ return df
67
+ except Exception as e:
68
+ raise ValueError(f"Data type conversion failed: {str(e)}")
69
+
70
+ @staticmethod
71
+ def validate_ranges(df: pd.DataFrame) -> None:
72
+ if not df['ph_level'].between(0, 14).all():
73
+ raise ValueError("pH values must be between 0 and 14")
74
+ if (df[['wastewater_lmin', 'co2_emission_kg', 'energy_kwh']] < 0).any().any():
75
+ raise ValueError("Environmental measurements cannot be negative")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
76
 
77
+ # Data Management
78
+ class DataManager:
79
+ @staticmethod
80
+ def get_sample_data() -> str:
81
+ return """timestamp,ph_level,wastewater_lmin,co2_emission_kg,energy_kwh,chemical_usage_kg,location,status
82
  2024-08-27 14:30,7.4,48.5,14.2,92,2.3,Statoil Platform Alpha,compliant
83
  2024-08-27 14:25,7.2,45.1,12.8,88,2.1,Statoil Platform Alpha,compliant
84
  2024-08-27 14:20,7.6,52.3,15.7,95,2.5,Statoil Platform Alpha,warning
85
  2024-08-27 14:15,7.8,55.2,16.1,98,2.8,Statoil Platform Alpha,violation
86
+ 2024-08-27 14:10,7.3,47.8,13.5,89,2.2,Statoil Platform Alpha,compliant"""
 
 
 
 
 
 
 
 
 
87
 
88
+ @staticmethod
89
+ @st.cache_data
90
+ def load_and_validate_data(uploaded_file) -> pd.DataFrame:
91
+ try:
92
+ if uploaded_file:
93
+ df = pd.read_csv(uploaded_file)
94
+ else:
95
+ df = pd.read_csv(StringIO(DataManager.get_sample_data()))
96
+
97
+ DataValidator.validate_csv_structure(df)
98
+ df = DataValidator.validate_data_types(df)
99
+ DataValidator.validate_ranges(df)
100
+ return df
101
+
102
+ except Exception as e:
103
+ st.error(f"Data loading error: {str(e)}")
104
+ return pd.DataFrame()
105
+
106
+ @staticmethod
107
+ def get_carbon_footprint_data() -> pd.DataFrame:
108
+ return pd.DataFrame({
109
+ 'source': ['Fuel Consumption', 'Electricity', 'Chemicals', 'Transport'],
110
+ 'co2_kg': [145.2, 67.8, 89.1, 19.6],
111
+ 'percentage': [45, 21, 28, 6]
112
+ })
 
 
113
 
114
+ # UI Components
115
+ class UIComponents:
116
+ @staticmethod
117
+ def apply_styling():
118
+ st.markdown("""
 
 
 
119
  <style>
120
+ .main-header {
121
+ background: linear-gradient(135deg, #1e40af 0%, #059669 100%);
122
+ padding: 2rem; border-radius: 15px; margin-bottom: 2rem;
123
+ color: white; text-align: center; box-shadow: 0 8px 32px rgba(0,0,0,0.1);
124
+ }
125
+ .metric-card {
126
+ background: white; padding: 1.5rem; border-radius: 12px;
127
+ border: 2px solid #e5e7eb; box-shadow: 0 4px 20px rgba(0,0,0,0.08);
128
+ text-align: center; transition: transform 0.2s ease;
129
+ }
130
+ .metric-card:hover { transform: translateY(-2px); }
131
+ .alert-danger {
132
+ background: linear-gradient(135deg, #fef2f2 0%, #fee2e2 100%);
133
+ border-left: 4px solid #dc2626; padding: 1rem; border-radius: 8px; margin: 1rem 0;
134
+ }
135
+ .alert-success {
136
+ background: linear-gradient(135deg, #f0fdf4 0%, #dcfce7 100%);
137
+ border-left: 4px solid #059669; padding: 1rem; border-radius: 8px; margin: 1rem 0;
138
+ }
139
+ .alert-warning {
140
+ background: linear-gradient(135deg, #fffbeb 0%, #fef3c7 100%);
141
+ border-left: 4px solid #f59e0b; padding: 1rem; border-radius: 8px; margin: 1rem 0;
142
+ }
143
+ .chart-container {
144
+ background: white; padding: 1.5rem; border-radius: 12px;
145
+ box-shadow: 0 4px 20px rgba(0,0,0,0.08); margin: 1rem 0; border: 1px solid #f1f5f9;
146
+ }
147
  </style>
148
+ """, unsafe_allow_html=True)
149
+
150
+ @staticmethod
151
+ def render_header():
152
+ st.markdown("""
153
+ <div class="main-header">
154
+ <h1>🌱 ESG Compliance Intelligence</h1>
155
+ <p>Norwegian Petroleum Safety Authority Real-time Monitoring</p>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
156
  </div>
157
+ """, unsafe_allow_html=True)
158
+
159
+ @staticmethod
160
+ def render_alert(status: str, location: str):
161
+ alerts = {
162
+ 'violation': ('alert-danger', '🚨', 'CRITICAL ALERT', 'Environmental violation detected'),
163
+ 'warning': ('alert-warning', '⚠️', 'WARNING', 'Parameters approaching limits'),
164
+ 'compliant': ('alert-success', 'βœ…', 'COMPLIANT', 'All systems within regulations')
165
+ }
166
 
167
+ css_class, icon, title, message = alerts.get(status, alerts['compliant'])
168
+ st.markdown(f"""
169
+ <div class="{css_class}">
170
+ {icon} <strong>{title}:</strong> {message} at {location}
 
 
171
  </div>
172
+ """, unsafe_allow_html=True)
 
 
 
173
 
174
+ # Visualization
175
+ class ChartGenerator:
176
+ @staticmethod
177
+ def create_environmental_trend(df: pd.DataFrame):
178
+ fig = px.line(df, x='timestamp', y=['ph_level', 'wastewater_lmin'],
179
+ title="Environmental Parameters Over Time")
180
+ fig.update_layout(height=400, showlegend=True)
181
+ return fig
 
 
 
 
 
 
182
 
183
+ @staticmethod
184
+ def create_carbon_breakdown(carbon_df: pd.DataFrame):
185
+ fig = px.bar(carbon_df, x='source', y='co2_kg',
186
+ title="COβ‚‚ Emissions by Source",
187
+ color='co2_kg', color_continuous_scale='Greens')
188
+ fig.update_layout(height=400)
189
+ return fig
 
 
 
 
 
 
 
 
 
190
 
191
+ @staticmethod
192
+ def create_compliance_trend(df: pd.DataFrame):
193
+ compliance_map = {'compliant': 100, 'warning': 70, 'violation': 30}
194
+ df['compliance_score'] = df['status'].map(compliance_map)
 
 
 
 
 
195
 
196
+ fig = px.area(df, x='timestamp', y='compliance_score',
197
+ title="Compliance Score Trend",
198
+ color_discrete_sequence=['#059669'])
199
+ fig.add_hline(y=Config.COMPLIANCE_THRESHOLD, line_dash="dash",
200
+ line_color="red", annotation_text="PSA Threshold")
201
+ fig.update_layout(height=300)
202
+ return fig
203
+
204
+ # Report Generation
205
+ class ReportGenerator:
206
+ @staticmethod
207
+ def generate_html_report(report: ComplianceReport) -> str:
208
+ return f"""
209
+ <!DOCTYPE html>
210
+ <html>
211
+ <head>
212
+ <meta charset="UTF-8">
213
+ <title>PSA Environmental Compliance Report</title>
214
+ <style>
215
+ body {{ font-family: Arial, sans-serif; margin: 40px; }}
216
+ .header {{ background: #1e40af; color: white; padding: 20px; border-radius: 8px; text-align: center; }}
217
+ .section {{ margin: 20px 0; padding: 15px; border: 1px solid #ddd; border-radius: 8px; }}
218
+ .compliant {{ color: #059669; font-weight: bold; }}
219
+ .warning {{ color: #f59e0b; font-weight: bold; }}
220
+ .violation {{ color: #dc2626; font-weight: bold; }}
221
+ table {{ width: 100%; border-collapse: collapse; margin: 10px 0; }}
222
+ th, td {{ border: 1px solid #ddd; padding: 8px; text-align: left; }}
223
+ th {{ background-color: #f8fafc; }}
224
+ </style>
225
+ </head>
226
+ <body>
227
+ <div class="header">
228
+ <h1>Norwegian Petroleum Safety Authority</h1>
229
+ <h2>Environmental Compliance Report</h2>
230
+ <p>Report ID: {report.report_id}</p>
231
+ </div>
232
 
233
+ <div class="section">
234
+ <h3>Summary</h3>
235
+ <p><strong>Generated:</strong> {report.timestamp}</p>
236
+ <p><strong>Location:</strong> {report.location}</p>
237
+ <p><strong>Status:</strong> <span class="{report.compliance_status.lower()}">{report.compliance_status}</span></p>
238
+ </div>
 
 
239
 
240
+ <div class="section">
241
+ <h3>Environmental Measurements</h3>
242
+ <table>
243
+ <tr><th>Parameter</th><th>Value</th><th>Unit</th><th>Status</th></tr>
244
+ <tr><td>pH Level</td><td>{report.ph_level:.1f}</td><td>pH</td><td>βœ“ Monitored</td></tr>
245
+ <tr><td>Wastewater</td><td>{report.wastewater_volume:.1f}</td><td>L/min</td><td>βœ“ Monitored</td></tr>
246
+ <tr><td>COβ‚‚ Emissions</td><td>{report.co2_total:.1f}</td><td>kg</td><td>βœ“ Tracked</td></tr>
247
+ <tr><td>Energy</td><td>{report.energy_consumption:.1f}</td><td>kWh</td><td>βœ“ Tracked</td></tr>
248
+ </table>
249
+ </div>
250
+
251
+ <div class="section">
252
+ <h3>Certification</h3>
253
+ <p>Report generated by {report.generated_by}</p>
254
+ <p><strong>Hash:</strong> {hashlib.md5(str(report.report_id).encode()).hexdigest()[:8]}</p>
255
+ </div>
256
+ </body>
257
+ </html>
258
+ """
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
259
 
260
+ @staticmethod
261
+ def create_excel_export(df: pd.DataFrame, carbon_df: pd.DataFrame) -> bytes:
262
+ output = BytesIO()
263
+ with pd.ExcelWriter(output, engine='openpyxl') as writer:
264
+ df.to_excel(writer, sheet_name='Environmental_Data', index=False)
265
+ carbon_df.to_excel(writer, sheet_name='Carbon_Footprint', index=False)
266
+
267
+ summary = pd.DataFrame({
268
+ 'Metric': ['Latest pH', 'Latest COβ‚‚', 'Latest Energy', 'Status'],
269
+ 'Value': [df.iloc[-1]['ph_level'], df.iloc[-1]['co2_emission_kg'],
270
+ df.iloc[-1]['energy_kwh'], df.iloc[-1]['status'].upper()]
271
+ })
272
+ summary.to_excel(writer, sheet_name='Summary', index=False)
273
+
274
+ return output.getvalue()
275
+
276
+ # Main Application
277
+ class ESGDashboard:
278
+ def __init__(self):
279
+ st.set_page_config(
280
+ page_title=Config.PAGE_TITLE,
281
+ page_icon=Config.PAGE_ICON,
282
+ layout="wide"
283
  )
284
+ UIComponents.apply_styling()
285
 
286
+ def render_sidebar(self) -> Optional[pd.DataFrame]:
287
+ with st.sidebar:
288
+ st.header("βš™οΈ Controls")
289
+
290
+ uploaded_file = st.file_uploader("Upload CSV Data", type=['csv'])
291
+
292
+ if st.button("πŸ”„ Refresh Data"):
293
+ st.cache_data.clear()
294
+ st.rerun()
295
+
296
+ st.markdown("---")
297
+ st.markdown("**Required CSV Format:**")
298
+ st.code("timestamp,ph_level,wastewater_lmin,co2_emission_kg,energy_kwh,location,status")
299
+
300
+ return uploaded_file
301
 
302
+ def render_metrics(self, df: pd.DataFrame):
303
+ latest = df.iloc[-1]
304
+ col1, col2, col3, col4 = st.columns(4)
305
+
306
+ status_icons = {"compliant": "βœ…", "warning": "⚠️", "violation": "🚨"}
307
+
308
+ with col1:
309
+ st.metric("Status", f"{status_icons[latest['status']]} {latest['status'].upper()}")
310
+ with col2:
311
+ ph_delta = latest['ph_level'] - 7.0
312
+ st.metric("pH Level", f"{latest['ph_level']:.1f}", f"{ph_delta:+.1f}")
313
+ with col3:
314
+ st.metric("COβ‚‚ Emissions", f"{latest['co2_emission_kg']:.1f} kg", "-2.3 kg")
315
+ with col4:
316
+ st.metric("Energy Usage", f"{latest['energy_kwh']:.0f} kWh", "+5.2 kWh")
317
 
318
+ def render_charts(self, df: pd.DataFrame, carbon_df: pd.DataFrame):
319
+ col1, col2 = st.columns(2)
320
+
321
+ with col1:
322
+ st.markdown('<div class="chart-container">', unsafe_allow_html=True)
323
+ st.subheader("πŸ’§ Environmental Monitoring")
324
+ fig_env = ChartGenerator.create_environmental_trend(df)
325
+ st.plotly_chart(fig_env, use_container_width=True)
326
+ st.markdown('</div>', unsafe_allow_html=True)
327
+
328
+ with col2:
329
+ st.markdown('<div class="chart-container">', unsafe_allow_html=True)
330
+ st.subheader("πŸ“ˆ Carbon Footprint")
331
+ fig_carbon = ChartGenerator.create_carbon_breakdown(carbon_df)
332
+ st.plotly_chart(fig_carbon, use_container_width=True)
333
+ st.markdown('</div>', unsafe_allow_html=True)
334
 
335
+ st.markdown('<div class="chart-container">', unsafe_allow_html=True)
336
+ st.subheader("πŸ“Š Compliance Trend")
337
+ fig_compliance = ChartGenerator.create_compliance_trend(df)
338
+ st.plotly_chart(fig_compliance, use_container_width=True)
 
339
  st.markdown('</div>', unsafe_allow_html=True)
340
 
341
+ def render_export_section(self, df: pd.DataFrame, carbon_df: pd.DataFrame):
342
  st.markdown('<div class="chart-container">', unsafe_allow_html=True)
343
+ st.subheader("⬇️ Export Reports")
344
+
345
+ col1, col2, col3 = st.columns(3)
346
+
347
+ with col1:
348
+ if st.button("πŸ“„ Generate PDF Report", type="primary"):
349
+ report = ComplianceReport.from_dataframe_row(df.iloc[-1])
350
+ html_content = ReportGenerator.generate_html_report(report)
351
+
352
+ st.download_button(
353
+ label="⬇️ Download Report",
354
+ data=html_content,
355
+ file_name=f"PSA_Report_{datetime.now().strftime('%Y%m%d_%H%M')}.html",
356
+ mime="text/html"
357
+ )
358
+ st.success("βœ… Report ready for download!")
359
+
360
+ with col2:
361
+ if st.button("πŸ“Š Export Excel"):
362
+ excel_data = ReportGenerator.create_excel_export(df, carbon_df)
363
+
364
+ st.download_button(
365
+ label="⬇️ Download Excel",
366
+ data=excel_data,
367
+ file_name=f"ESG_Data_{datetime.now().strftime('%Y%m%d_%H%M')}.xlsx",
368
+ mime="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
369
+ )
370
+ st.success("βœ… Excel file ready!")
371
+
372
+ with col3:
373
+ if st.button("πŸ“€ Submit to PSA"):
374
+ submission_id = f"PSA-SUB-{datetime.now().strftime('%Y%m%d%H%M')}"
375
+ st.success(f"βœ… Submitted!\nID: {submission_id}")
376
 
 
 
 
 
 
 
377
  st.markdown('</div>', unsafe_allow_html=True)
378
 
379
+ def run(self):
380
+ UIComponents.render_header()
381
+
382
+ uploaded_file = self.render_sidebar()
383
+
384
+ # Load and validate data
385
+ df = DataManager.load_and_validate_data(uploaded_file)
386
+ if df.empty:
387
+ st.stop()
388
+
389
+ carbon_df = DataManager.get_carbon_footprint_data()
390
+
391
+ # Render alert
392
+ latest_status = df.iloc[-1]['status']
393
+ latest_location = df.iloc[-1]['location']
394
+ UIComponents.render_alert(latest_status, latest_location)
395
+
396
+ # Render dashboard components
397
+ self.render_metrics(df)
398
+ self.render_charts(df, carbon_df)
399
+ self.render_export_section(df, carbon_df)
400
+
401
+ # Application Entry Point
402
+ def main():
403
+ dashboard = ESGDashboard()
404
+ dashboard.run()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
405
 
406
  if __name__ == "__main__":
407
  main()