k96beni commited on
Commit
3f207ba
·
verified ·
1 Parent(s): 1a170e3

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +16 -303
app.py CHANGED
@@ -1110,75 +1110,19 @@ def batch_update_cells_with_tracking(sheet, changes, df, editor_email, log_sheet
1110
  else:
1111
  return False, f"❌ Fel vid sparande: {error_msg}. Kontakta migration@chargenode.eu om problemet kvarstår."
1112
 
1113
- def calculate_completion_stats(df, editable_cols):
1114
- """Beräkna kompletteringsstatistik för editerbara kolumner"""
1115
- stats = {}
1116
-
1117
- for col in editable_cols:
1118
- if col in df.columns:
1119
- total = len(df)
1120
- # Räkna celler som har värde (inte nan, inte tom sträng)
1121
- filled = df[col].apply(lambda x: bool(x and str(x).strip() != '')).sum()
1122
- stats[col] = {
1123
- 'filled': filled,
1124
- 'total': total,
1125
- 'percentage': (filled / total * 100) if total > 0 else 0
1126
- }
1127
-
1128
- # Totala stats
1129
- total_cells = sum(s['total'] for s in stats.values())
1130
- total_filled = sum(s['filled'] for s in stats.values())
1131
- overall_percentage = (total_filled / total_cells * 100) if total_cells > 0 else 0
1132
-
1133
- return {
1134
- 'by_column': stats,
1135
- 'overall': {
1136
- 'filled': total_filled,
1137
- 'total': total_cells,
1138
- 'percentage': overall_percentage
1139
- }
1140
- }
1141
-
1142
- def get_incomplete_rows(df, editable_cols):
1143
- """Hitta rader där minst ett editerbart fält är tomt"""
1144
- incomplete_mask = pd.Series([False] * len(df), index=df.index)
1145
-
1146
- for col in editable_cols:
1147
- if col in df.columns:
1148
- # Markera rader där cellen är tom
1149
- empty_mask = df[col].apply(lambda x: not bool(x and str(x).strip() != ''))
1150
- incomplete_mask = incomplete_mask | empty_mask
1151
-
1152
- return incomplete_mask
1153
-
1154
- def style_empty_cells(df, editable_cols):
1155
- """Returnera styling-funktion för att markera tomma celler"""
1156
- def highlight_empty(row):
1157
- styles = [''] * len(row)
1158
- for idx, (col_name, value) in enumerate(row.items()):
1159
- if col_name in editable_cols:
1160
- # Markera om värdet är tomt eller None
1161
- if pd.isna(value) or str(value).strip() == '' or str(value).strip() == '0':
1162
- styles[idx] = 'background-color: #FFE5E5; border: 2px solid #DC3545;'
1163
- return styles
1164
-
1165
- return highlight_empty
1166
-
1167
  def get_column_config(df):
1168
  """Generera kolumnkonfiguration för data_editor med beskrivningar och dropdowns"""
1169
  config = {}
1170
 
1171
- # Beskrivningar för editerbara kolumner - med gemensam suffix
1172
- base_suffix = "\n\n🔺 Vänligen fyll i detta fält. Data uppdateras automatiskt."
1173
-
1174
  editable_descriptions = {
1175
- 'Namn': 'Huvudkontaktperson för detta område. Personen vi kontaktar vid frågor eller problem.' + base_suffix,
1176
- 'Email adress': 'Ange individuell e-postadress till kontaktpersonen för detta område.' + base_suffix,
1177
- 'Telefon': 'Telefonnummer till huvudkontaktpersonen. Format: +46XXXXXXXXX eller 07X-XXXXXXX' + base_suffix,
1178
- 'Tillgänglighet': 'Välj i dropdown-menyn om detta är ett område som vi alltid kan komma åt, eller om det är stängt. Om det är stängt var vänlig fyll i fältet Access info.' + base_suffix,
1179
- 'Access info': 'Om området inte alltid är tillgängligt, vänligen ange hur vi kan få tillgång. T.ex. "Nycklar finns i receptionen" eller "Ring Pelle på 07XXXXXXXX när ni är på gång".' + base_suffix,
1180
- 'Fritext meddelande': 'Övrig information eller meddelanden som är viktiga för oss att veta.' + base_suffix,
1181
- 'Main Contact': 'Ange om denna person är huvudansvarig för kontot. Välj "Huvudkontakt" för helst endast en person per företag.' + base_suffix
1182
  }
1183
 
1184
  # Beskrivningar för readonly kolumner
@@ -1202,7 +1146,7 @@ def get_column_config(df):
1202
  if col == 'Tillgänglighet':
1203
  # Dropdown för tillgänglighet
1204
  config[col] = st.column_config.SelectboxColumn(
1205
- f"🔺 {col}",
1206
  help=editable_descriptions.get(col, ''),
1207
  width="medium",
1208
  options=[
@@ -1214,7 +1158,7 @@ def get_column_config(df):
1214
  elif col == 'Main Contact':
1215
  # Dropdown för huvudkontakt
1216
  config[col] = st.column_config.SelectboxColumn(
1217
- f"🔺 {col}",
1218
  help=editable_descriptions.get(col, ''),
1219
  width="medium",
1220
  options=[
@@ -1226,7 +1170,7 @@ def get_column_config(df):
1226
  else:
1227
  # Vanlig textkolumn
1228
  config[col] = st.column_config.TextColumn(
1229
- f"🔺 {col}",
1230
  help=editable_descriptions.get(col, ''),
1231
  width="medium",
1232
  required=False
@@ -1263,76 +1207,6 @@ st.markdown("""
1263
  padding-bottom: 0rem;
1264
  }
1265
 
1266
- /* Röd triangel-indikator för editerbara kolumner */
1267
- /* Markera editerbara kolumnrubriker */
1268
- [data-testid="stDataFrame"] thead th[aria-label*="🔺"] {
1269
- position: relative;
1270
- background-color: #FFF4E6 !important;
1271
- border: 2px solid #FF9800 !important;
1272
- font-weight: bold !important;
1273
- }
1274
-
1275
- [data-testid="stDataFrame"] thead th[aria-label*="🔺"]::after {
1276
- content: "";
1277
- position: absolute;
1278
- top: 0;
1279
- right: 0;
1280
- width: 0;
1281
- height: 0;
1282
- border-style: solid;
1283
- border-width: 0 15px 15px 0;
1284
- border-color: transparent #DC3545 transparent transparent;
1285
- z-index: 10;
1286
- }
1287
-
1288
- /* Hover-effekt för editerbara kolumnrubriker */
1289
- [data-testid="stDataFrame"] thead th[aria-label*="🔺"]:hover {
1290
- background-color: #FFE5CC !important;
1291
- }
1292
-
1293
- /* VIKTIGT: Markera alla CELLER i editerbara kolumner med ljusorange bakgrund */
1294
- /* Vi använder nth-child för att rikta in oss på specifika kolumner */
1295
- /* Detta måste justeras baserat på vilka kolumner som är editerbara */
1296
- [data-testid="stDataFrame"] tbody td {
1297
- position: relative;
1298
- }
1299
-
1300
- /* Lägg till en subtil border på alla celler för bättre läsbarhet */
1301
- [data-testid="stDataFrame"] td,
1302
- [data-testid="stDataFrame"] th {
1303
- border: 1px solid #E0E0E0 !important;
1304
- }
1305
-
1306
- /* Styla editerbara celler i data_editor - de som inte är disabled */
1307
- div[data-testid="stDataFrameResizable"] input:not(:disabled),
1308
- div[data-testid="stDataFrameResizable"] textarea:not(:disabled) {
1309
- background-color: #FFF9F0 !important;
1310
- border: 2px solid #FFB74D !important;
1311
- border-radius: 4px;
1312
- }
1313
-
1314
- /* Hover-effekt på editerbara celler */
1315
- div[data-testid="stDataFrameResizable"] input:not(:disabled):hover,
1316
- div[data-testid="stDataFrameResizable"] textarea:not(:disabled):hover {
1317
- background-color: #FFF3E0 !important;
1318
- border-color: #FF9800 !important;
1319
- }
1320
-
1321
- /* Focus-effekt på editerbara celler */
1322
- div[data-testid="stDataFrameResizable"] input:not(:disabled):focus,
1323
- div[data-testid="stDataFrameResizable"] textarea:not(:disabled):focus {
1324
- background-color: #FFFFFF !important;
1325
- border-color: #FF5722 !important;
1326
- box-shadow: 0 0 0 2px rgba(255, 87, 34, 0.2) !important;
1327
- }
1328
-
1329
- /* Disabled/readonly celler får neutral färg */
1330
- div[data-testid="stDataFrameResizable"] input:disabled,
1331
- div[data-testid="stDataFrameResizable"] textarea:disabled {
1332
- background-color: #F5F5F5 !important;
1333
- border: 1px solid #E0E0E0 !important;
1334
- }
1335
-
1336
  .excel-info-box {
1337
  background-color: #E3F2FD;
1338
  border-left: 4px solid #2196F3;
@@ -1967,176 +1841,24 @@ else:
1967
  st.markdown(f"*...och {len(visible_readonly) - 15} fält till*")
1968
 
1969
  with col_help2:
1970
- st.markdown("#### 🔺 Editerbara fält")
1971
- st.markdown("Dessa fält har **🔺 (röd triangel)** i kolumnrubriken och kan redigeras. Håll muspekaren över kolumnnamnet för att se detaljerad hjälptext:")
1972
  visible_editable = [col for col in EDITABLE_COLUMNS if col not in HIDDEN_COLUMNS]
1973
  for col in visible_editable:
1974
  if col in company_areas.columns:
1975
- st.markdown(f"• 🔺 {col}")
1976
 
1977
  # Huvudtabell
1978
  st.markdown("## 📊 Dataöversikt")
1979
  st.markdown("""
1980
- Kolumner med **🔺** (röd triangel) är editerbara - klicka i fälten för att fylla i eller redigera. Kolumner med **🔒** (gult hänglås) kan inte editeras. **Ändringar sparas automatiskt.**
1981
 
1982
  **Kontrollera att uppgifterna i låsta celler stämmer.** Om något verkar fel, mejla till [migration@chargenode.eu](mailto:migration@chargenode.eu) så hjälper vi dig.
1983
  """)
1984
-
1985
- # ============================================================================
1986
- # KOMPLETTERINGSSTATISTIK - Ny sektion
1987
- # ============================================================================
1988
- st.markdown("---")
1989
- st.markdown("### 📈 Kompletteringsstatus")
1990
-
1991
- # Beräkna komplettering
1992
- visible_editable = [col for col in EDITABLE_COLUMNS if col in company_areas.columns and col not in HIDDEN_COLUMNS]
1993
-
1994
- if visible_editable:
1995
- # Total komplettering
1996
- total_cells = len(company_areas) * len(visible_editable)
1997
- filled_cells = 0
1998
-
1999
- for col in visible_editable:
2000
- filled_cells += company_areas[col].notna().sum()
2001
- # Räkna också icke-tomma strängar
2002
- filled_cells += (company_areas[col].astype(str).str.strip() != '').sum()
2003
- # Dra bort dubbelräkning
2004
- filled_cells -= company_areas[col].notna().sum()
2005
-
2006
- # Bättre beräkning: räkna celler som har värde OCH inte är tomma strängar
2007
- filled_cells = 0
2008
- for col in visible_editable:
2009
- filled_cells += ((company_areas[col].notna()) & (company_areas[col].astype(str).str.strip() != '')).sum()
2010
-
2011
- completion_pct = (filled_cells / total_cells * 100) if total_cells > 0 else 0
2012
-
2013
- # Metrics i kolumner
2014
- metric_col1, metric_col2, metric_col3, metric_col4 = st.columns(4)
2015
-
2016
- with metric_col1:
2017
- st.metric(
2018
- label="✅ Kompletteringsgrad",
2019
- value=f"{completion_pct:.0f}%",
2020
- help="Procent av alla editerbara fält som är ifyllda"
2021
- )
2022
-
2023
- with metric_col2:
2024
- st.metric(
2025
- label="📝 Ifyllda fält",
2026
- value=f"{filled_cells}/{total_cells}",
2027
- help="Antal ifyllda fält av totalt antal editerbara fält"
2028
- )
2029
-
2030
- with metric_col3:
2031
- # Räkna kompletta rader (alla editerbara fält ifyllda)
2032
- complete_rows = 0
2033
- for idx in range(len(company_areas)):
2034
- row_complete = True
2035
- for col in visible_editable:
2036
- val = company_areas.iloc[idx][col]
2037
- if pd.isna(val) or str(val).strip() == '':
2038
- row_complete = False
2039
- break
2040
- if row_complete:
2041
- complete_rows += 1
2042
-
2043
- st.metric(
2044
- label="✨ Kompletta områden",
2045
- value=f"{complete_rows}/{len(company_areas)}",
2046
- help="Områden där alla editerbara fält är ifyllda"
2047
- )
2048
-
2049
- with metric_col4:
2050
- incomplete_rows = len(company_areas) - complete_rows
2051
- st.metric(
2052
- label="⚠️ Ofullständiga områden",
2053
- value=incomplete_rows,
2054
- help="Områden som behöver mer information"
2055
- )
2056
-
2057
- # Progress bar
2058
- st.progress(completion_pct / 100, text=f"Total komplettering: {completion_pct:.1f}%")
2059
-
2060
- # Status per kolumn - Expanderbar sektion
2061
- with st.expander("📋 Detaljerad status per fält", expanded=False):
2062
- st.markdown("**Se hur många områden som har fyllt i varje fält:**")
2063
-
2064
- for col in visible_editable:
2065
- filled = ((company_areas[col].notna()) & (company_areas[col].astype(str).str.strip() != '')).sum()
2066
- total = len(company_areas)
2067
- pct = (filled / total * 100) if total > 0 else 0
2068
-
2069
- # Färgkod baserat på komplettering
2070
- if pct >= 90:
2071
- status_emoji = "🟢"
2072
- elif pct >= 50:
2073
- status_emoji = "🟡"
2074
- else:
2075
- status_emoji = "🔴"
2076
-
2077
- col_left, col_right = st.columns([4, 1])
2078
- with col_left:
2079
- st.progress(pct / 100, text=f"{status_emoji} {col}")
2080
- with col_right:
2081
- st.caption(f"{filled}/{total} ({pct:.0f}%)")
2082
-
2083
- # Filter-sektion
2084
- st.markdown("---")
2085
- filter_col1, filter_col2 = st.columns([2, 3])
2086
-
2087
- with filter_col1:
2088
- view_filter = st.radio(
2089
- "🔍 Visa:",
2090
- ["📋 Alla områden", "⚠️ Endast ofullständiga", "✅ Endast kompletta"],
2091
- horizontal=False,
2092
- help="Filtrera tabellen för att fokusera på det som behöver göras"
2093
- )
2094
-
2095
- with filter_col2:
2096
- if view_filter == "⚠️ Endast ofullständiga":
2097
- st.info("💡 **Tips:** Fokusera på att fylla i de röd-markerade fälten i dessa områden först!")
2098
- elif view_filter == "✅ Endast kompletta":
2099
- st.success("🎉 **Bra jobbat!** Dessa områden har all nödvändig information ifylld.")
2100
- else:
2101
- st.info(f"📊 Visar alla {len(company_areas)} områden")
2102
-
2103
  st.markdown("<br>", unsafe_allow_html=True)
2104
 
2105
- # Applicera filter baserat på användarens val
2106
- company_areas_filtered = company_areas.copy()
2107
-
2108
- if visible_editable and 'view_filter' in locals():
2109
- if view_filter == "⚠️ Endast ofullständiga":
2110
- # Filtrera till rader där minst ett editerbart fält är tomt
2111
- mask = pd.Series([False] * len(company_areas))
2112
- for idx in range(len(company_areas)):
2113
- for col in visible_editable:
2114
- val = company_areas.iloc[idx][col]
2115
- if pd.isna(val) or str(val).strip() == '':
2116
- mask.iloc[idx] = True
2117
- break
2118
- company_areas_filtered = company_areas[mask]
2119
-
2120
- if len(company_areas_filtered) == 0:
2121
- st.success("🎉 Fantastiskt! Alla områden är kompletta!")
2122
- st.balloons()
2123
-
2124
- elif view_filter == "✅ Endast kompletta":
2125
- # Filtrera till rader där alla editerbara fält är ifyllda
2126
- mask = pd.Series([True] * len(company_areas))
2127
- for idx in range(len(company_areas)):
2128
- for col in visible_editable:
2129
- val = company_areas.iloc[idx][col]
2130
- if pd.isna(val) or str(val).strip() == '':
2131
- mask.iloc[idx] = False
2132
- break
2133
- company_areas_filtered = company_areas[mask]
2134
-
2135
- if len(company_areas_filtered) == 0:
2136
- st.warning("⚠️ Inga kompletta områden än. Börja fylla i de editerbara fälten!")
2137
-
2138
  # Ta bort dolda kolumner och sätt kolumnordning med Fastighetsnamn först
2139
- company_areas_display = company_areas_filtered.drop(columns=HIDDEN_COLUMNS, errors='ignore')
2140
 
2141
  # Skapa kolumnordning med Fastighetsnamn först (för bättre synlighet vid scrolling)
2142
  column_order = ['Fastighetsnamn'] + [col for col in company_areas_display.columns if col != 'Fastighetsnamn']
@@ -2144,15 +1866,6 @@ else:
2144
 
2145
  column_config = get_column_config(company_areas_display)
2146
 
2147
- # Visa antal rader som visas
2148
- if len(company_areas_display) > 0:
2149
- st.markdown(f"""
2150
- <div style='background-color: #E3F2FD; padding: 1rem; border-radius: 8px; margin: 1rem 0; border-left: 4px solid #2196F3;'>
2151
- <strong>📍 Visar {len(company_areas_display)} av {len(company_areas)} områden</strong><br>
2152
- <small>💡 <strong>Tips:</strong> Editerbara celler (🔺) har orange bakgrund och ram. Klicka i en cell för att börja redigera. Ändringar sparas automatiskt.</small>
2153
- </div>
2154
- """, unsafe_allow_html=True)
2155
-
2156
  # Container för att förhindra scroll-hopp
2157
  table_container = st.container()
2158
 
 
1110
  else:
1111
  return False, f"❌ Fel vid sparande: {error_msg}. Kontakta migration@chargenode.eu om problemet kvarstår."
1112
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1113
  def get_column_config(df):
1114
  """Generera kolumnkonfiguration för data_editor med beskrivningar och dropdowns"""
1115
  config = {}
1116
 
1117
+ # Beskrivningar för editerbara kolumner
 
 
1118
  editable_descriptions = {
1119
+ 'Namn': 'Huvudkontaktperson för detta område. Personen vi kontaktar vid frågor eller problem.',
1120
+ 'Email adress': 'Ange individuell e-postadress till kontaktpersonen för detta område.',
1121
+ 'Telefon': 'Telefonnummer till huvudkontaktpersonen. Format: +46XXXXXXXXX eller 07X-XXXXXXX',
1122
+ 'Tillgänglighet': 'Välj i dropdown-menyn om detta är ett område som vi alltid kan komma åt, eller om det är stängt. Om det är stängt var vänlig fyll i fältet Access info.',
1123
+ 'Access info': 'Om området inte alltid är tillgängligt, vänligen ange hur vi kan få tillgång. T.ex. "Nycklar finns i receptionen" eller "Ring Pelle på 07XXXXXXXX när ni är på gång".',
1124
+ 'Fritext meddelande': 'Övrig information eller meddelanden som är viktiga för oss att veta.',
1125
+ 'Main Contact': 'Ange om denna person är huvudansvarig för kontot. Välj "Huvudkontakt" för helst endast en person per företag.'
1126
  }
1127
 
1128
  # Beskrivningar för readonly kolumner
 
1146
  if col == 'Tillgänglighet':
1147
  # Dropdown för tillgänglighet
1148
  config[col] = st.column_config.SelectboxColumn(
1149
+ col,
1150
  help=editable_descriptions.get(col, ''),
1151
  width="medium",
1152
  options=[
 
1158
  elif col == 'Main Contact':
1159
  # Dropdown för huvudkontakt
1160
  config[col] = st.column_config.SelectboxColumn(
1161
+ col,
1162
  help=editable_descriptions.get(col, ''),
1163
  width="medium",
1164
  options=[
 
1170
  else:
1171
  # Vanlig textkolumn
1172
  config[col] = st.column_config.TextColumn(
1173
+ col,
1174
  help=editable_descriptions.get(col, ''),
1175
  width="medium",
1176
  required=False
 
1207
  padding-bottom: 0rem;
1208
  }
1209
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1210
  .excel-info-box {
1211
  background-color: #E3F2FD;
1212
  border-left: 4px solid #2196F3;
 
1841
  st.markdown(f"*...och {len(visible_readonly) - 15} fält till*")
1842
 
1843
  with col_help2:
1844
+ st.markdown("#### ✏️ Editerbara fält")
1845
+ st.markdown("Dessa fält kan du klicka i och redigera:")
1846
  visible_editable = [col for col in EDITABLE_COLUMNS if col not in HIDDEN_COLUMNS]
1847
  for col in visible_editable:
1848
  if col in company_areas.columns:
1849
+ st.markdown(f"• ✏️ {col}")
1850
 
1851
  # Huvudtabell
1852
  st.markdown("## 📊 Dataöversikt")
1853
  st.markdown("""
1854
+ Kolumner med **🔒** (gult hänglås) kan inte editeras. Klicka i andra fält för att redigera. **Ändringar sparas automatiskt.**
1855
 
1856
  **Kontrollera att uppgifterna i låsta celler stämmer.** Om något verkar fel, mejla till [migration@chargenode.eu](mailto:migration@chargenode.eu) så hjälper vi dig.
1857
  """)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1858
  st.markdown("<br>", unsafe_allow_html=True)
1859
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1860
  # Ta bort dolda kolumner och sätt kolumnordning med Fastighetsnamn först
1861
+ company_areas_display = company_areas.drop(columns=HIDDEN_COLUMNS, errors='ignore')
1862
 
1863
  # Skapa kolumnordning med Fastighetsnamn först (för bättre synlighet vid scrolling)
1864
  column_order = ['Fastighetsnamn'] + [col for col in company_areas_display.columns if col != 'Fastighetsnamn']
 
1866
 
1867
  column_config = get_column_config(company_areas_display)
1868
 
 
 
 
 
 
 
 
 
 
1869
  # Container för att förhindra scroll-hopp
1870
  table_container = st.container()
1871