k96beni commited on
Commit
4ab8fea
·
verified ·
1 Parent(s): 808b416

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +127 -102
app.py CHANGED
@@ -5,6 +5,7 @@ import pandas as pd
5
  from datetime import datetime
6
  import json
7
  import os
 
8
 
9
  # Konfigurera sidan
10
  st.set_page_config(
@@ -24,7 +25,8 @@ READONLY_COLUMNS = [
24
  'Tidstämpel',
25
  'Organisationsnummer',
26
  'Lösenord',
27
- 'Account', # Företagsnamn
 
28
  'Location Name',
29
  'Location - Address',
30
  'Long',
@@ -75,7 +77,6 @@ def get_sheet_data(sheet):
75
 
76
  def validate_password(df, password):
77
  """Validera lösenord och returnera organisationsnummer"""
78
- # Hitta rad där lösenord matchar
79
  match = df[df['Lösenord'].astype(str) == password]
80
 
81
  if not match.empty:
@@ -85,25 +86,44 @@ def validate_password(df, password):
85
 
86
  def get_company_areas(df, org_nr):
87
  """Hämta alla områden för ett organisationsnummer"""
88
- return df[df['Organisationsnummer'] == org_nr]
89
 
90
  def update_cell(sheet, row_num, col_name, new_value, df):
91
  """Uppdatera en specifik cell i Google Sheets"""
92
  try:
93
- # Hitta kolumnindex (1-baserad för gspread)
94
  col_idx = df.columns.get_loc(col_name) + 1
95
- # row_num är 0-baserad, lägg till 2 för header och 1-basering
96
  cell_row = row_num + 2
97
-
98
  sheet.update_cell(cell_row, col_idx, new_value)
99
  return True
100
  except Exception as e:
101
  st.error(f"❌ Kunde inte uppdatera {col_name}: {e}")
102
  return False
103
 
104
- def get_editable_columns(df):
105
- """Returnera lista med editerbara kolumner"""
106
- return [col for col in df.columns if col not in READONLY_COLUMNS]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
107
 
108
  # CSS för bättre styling
109
  st.markdown("""
@@ -111,18 +131,16 @@ st.markdown("""
111
  .stButton button {
112
  width: 100%;
113
  }
114
- .area-card {
115
- padding: 20px;
116
- border-radius: 10px;
117
- background-color: #f0f2f6;
118
- margin-bottom: 20px;
119
  }
120
- .success-msg {
121
  padding: 10px;
122
  border-radius: 5px;
123
  background-color: #d4edda;
124
  color: #155724;
125
  margin: 10px 0;
 
126
  }
127
  </style>
128
  """, unsafe_allow_html=True)
@@ -136,6 +154,7 @@ if 'authenticated' not in st.session_state:
136
  st.session_state.org_nr = None
137
  st.session_state.df = None
138
  st.session_state.company_name = None
 
139
 
140
  # Anslut till Google Sheets
141
  sheet = get_google_sheet()
@@ -173,6 +192,7 @@ if not st.session_state.authenticated:
173
  st.session_state.org_nr = org_nr
174
  st.session_state.df = df
175
  st.session_state.company_name = company_name
 
176
  st.success("✅ Inloggning lyckades!")
177
  st.rerun()
178
  else:
@@ -190,7 +210,7 @@ else:
190
  company_name = st.session_state.company_name
191
 
192
  # Header med företagsinfo
193
- col1, col2, col3 = st.columns([2, 1, 1])
194
 
195
  with col1:
196
  st.markdown(f"## 🏢 {company_name}")
@@ -199,9 +219,13 @@ else:
199
  with col2:
200
  if st.button("🔄 Uppdatera data", use_container_width=True):
201
  st.cache_resource.clear()
202
- st.session_state.df = get_sheet_data(sheet)
203
- st.success("✅ Data uppdaterad!")
204
- st.rerun()
 
 
 
 
205
 
206
  with col3:
207
  if st.button("🚪 Logga ut", use_container_width=True):
@@ -209,6 +233,7 @@ else:
209
  st.session_state.org_nr = None
210
  st.session_state.df = None
211
  st.session_state.company_name = None
 
212
  st.rerun()
213
 
214
  st.divider()
@@ -216,96 +241,96 @@ else:
216
  # Hämta alla områden för detta företag
217
  company_areas = get_company_areas(df, org_nr)
218
 
219
- st.markdown(f"### 📍 Dina områden ({len(company_areas)} st)")
 
220
 
221
- # Editerbara kolumner
222
- editable_cols = get_editable_columns(df)
 
 
 
 
 
 
 
 
 
 
 
223
 
224
- # Visa varje område i en expanderbar sektion
225
- for idx, (row_idx, area) in enumerate(company_areas.iterrows(), 1):
226
- with st.expander(f"🗺️ Område {idx}: {area['Områdeskod']} - {area['Location Name']}", expanded=(idx == 1)):
227
-
228
- # Visa fast information
229
- st.markdown("#### 📋 Fast information")
230
- info_cols = st.columns(3)
231
-
232
- with info_cols[0]:
233
- st.text_input("Områdeskod", value=area['Områdeskod'], disabled=True, key=f"area_{row_idx}_kod")
234
- st.text_input("Location Name", value=area['Location Name'], disabled=True, key=f"area_{row_idx}_locname")
235
-
236
- with info_cols[1]:
237
- st.text_input("Location Address", value=area['Location - Address'], disabled=True, key=f"area_{row_idx}_addr")
238
- st.text_input("Location City", value=area['Location - City'], disabled=True, key=f"area_{row_idx}_city")
239
-
240
- with info_cols[2]:
241
- st.text_input("Long", value=area['Long'], disabled=True, key=f"area_{row_idx}_long")
242
- st.text_input("Lat", value=area['Lat'], disabled=True, key=f"area_{row_idx}_lat")
243
-
244
- st.divider()
245
-
246
- # Editerbara fält
247
- st.markdown("#### ✏️ Editerbar information")
248
-
249
- # Gruppera editerbara fält
250
- edit_col1, edit_col2 = st.columns(2)
251
-
252
- changes = {} # Spara ändringar
253
-
254
- with edit_col1:
255
- # Första halvan av editerbara kolumner
256
- mid = len(editable_cols) // 2
257
- for col in editable_cols[:mid]:
258
- current_value = str(area[col]) if pd.notna(area[col]) else ""
259
-
260
- new_value = st.text_input(
261
- col,
262
- value=current_value,
263
- key=f"edit_{row_idx}_{col}"
264
- )
265
-
266
- if new_value != current_value:
267
- changes[col] = new_value
268
 
269
- with edit_col2:
270
- # Andra halvan av editerbara kolumner
271
- for col in editable_cols[mid:]:
272
- current_value = str(area[col]) if pd.notna(area[col]) else ""
273
-
274
- new_value = st.text_input(
275
- col,
276
- value=current_value,
277
- key=f"edit_{row_idx}_{col}"
278
- )
279
 
280
- if new_value != current_value:
281
- changes[col] = new_value
 
 
 
 
 
 
 
 
 
 
282
 
283
- # Om det finns ändringar, visa uppdateringsknapp
284
- if changes:
285
- st.warning(f"⚠️ {len(changes)} fält har ändrats")
286
 
287
- cols = st.columns([1, 1, 2])
288
- with cols[0]:
289
- if st.button(f"💾 Spara alla ändringar", key=f"save_all_{row_idx}", type="primary", use_container_width=True):
290
- success_count = 0
291
-
292
- with st.spinner("Sparar ändringar..."):
293
- for col, new_value in changes.items():
294
- if update_cell(sheet, row_idx, col, new_value, df):
295
- success_count += 1
296
-
297
- if success_count == len(changes):
298
- st.success(f"✅ Alla {success_count} ändringar sparade!")
299
- st.cache_resource.clear()
300
- st.session_state.df = get_sheet_data(sheet)
301
- st.rerun()
302
- else:
303
- st.error(f"⚠️ Kunde bara spara {success_count} av {len(changes)} ändringar")
304
 
305
- with cols[1]:
306
- if st.button(f"↩️ Ångra ändringar", key=f"reset_{row_idx}", use_container_width=True):
307
- st.rerun()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
308
 
309
  # Footer
310
  st.divider()
311
- st.caption(f"🔒 Säker åtkomst • Inloggad som {company_name} • {len(company_areas)} områden")
 
 
5
  from datetime import datetime
6
  import json
7
  import os
8
+ import time
9
 
10
  # Konfigurera sidan
11
  st.set_page_config(
 
25
  'Tidstämpel',
26
  'Organisationsnummer',
27
  'Lösenord',
28
+ 'Områdeskod',
29
+ 'Account',
30
  'Location Name',
31
  'Location - Address',
32
  'Long',
 
77
 
78
  def validate_password(df, password):
79
  """Validera lösenord och returnera organisationsnummer"""
 
80
  match = df[df['Lösenord'].astype(str) == password]
81
 
82
  if not match.empty:
 
86
 
87
  def get_company_areas(df, org_nr):
88
  """Hämta alla områden för ett organisationsnummer"""
89
+ return df[df['Organisationsnummer'] == org_nr].copy()
90
 
91
  def update_cell(sheet, row_num, col_name, new_value, df):
92
  """Uppdatera en specifik cell i Google Sheets"""
93
  try:
 
94
  col_idx = df.columns.get_loc(col_name) + 1
 
95
  cell_row = row_num + 2
 
96
  sheet.update_cell(cell_row, col_idx, new_value)
97
  return True
98
  except Exception as e:
99
  st.error(f"❌ Kunde inte uppdatera {col_name}: {e}")
100
  return False
101
 
102
+ def get_column_config(df):
103
+ """Skapa kolumnkonfiguration för data editor"""
104
+ config = {}
105
+ for col in df.columns:
106
+ if col in READONLY_COLUMNS:
107
+ # Skrivskyddade kolumner
108
+ if col == 'Lösenord':
109
+ config[col] = st.column_config.TextColumn(
110
+ col,
111
+ disabled=True,
112
+ help="Skrivskyddat fält"
113
+ )
114
+ else:
115
+ config[col] = st.column_config.TextColumn(
116
+ col,
117
+ disabled=True,
118
+ help="Skrivskyddat fält"
119
+ )
120
+ else:
121
+ # Editerbara kolumner
122
+ config[col] = st.column_config.TextColumn(
123
+ col,
124
+ help="Klicka för att editera"
125
+ )
126
+ return config
127
 
128
  # CSS för bättre styling
129
  st.markdown("""
 
131
  .stButton button {
132
  width: 100%;
133
  }
134
+ div[data-testid="stDataFrame"] {
135
+ width: 100%;
 
 
 
136
  }
137
+ .success-banner {
138
  padding: 10px;
139
  border-radius: 5px;
140
  background-color: #d4edda;
141
  color: #155724;
142
  margin: 10px 0;
143
+ text-align: center;
144
  }
145
  </style>
146
  """, unsafe_allow_html=True)
 
154
  st.session_state.org_nr = None
155
  st.session_state.df = None
156
  st.session_state.company_name = None
157
+ st.session_state.original_data = None
158
 
159
  # Anslut till Google Sheets
160
  sheet = get_google_sheet()
 
192
  st.session_state.org_nr = org_nr
193
  st.session_state.df = df
194
  st.session_state.company_name = company_name
195
+ st.session_state.original_data = company_areas.copy()
196
  st.success("✅ Inloggning lyckades!")
197
  st.rerun()
198
  else:
 
210
  company_name = st.session_state.company_name
211
 
212
  # Header med företagsinfo
213
+ col1, col2, col3 = st.columns([3, 1, 1])
214
 
215
  with col1:
216
  st.markdown(f"## 🏢 {company_name}")
 
219
  with col2:
220
  if st.button("🔄 Uppdatera data", use_container_width=True):
221
  st.cache_resource.clear()
222
+ new_df = get_sheet_data(sheet)
223
+ if new_df is not None:
224
+ st.session_state.df = new_df
225
+ company_areas = get_company_areas(new_df, org_nr)
226
+ st.session_state.original_data = company_areas.copy()
227
+ st.success("✅ Data uppdaterad!")
228
+ st.rerun()
229
 
230
  with col3:
231
  if st.button("🚪 Logga ut", use_container_width=True):
 
233
  st.session_state.org_nr = None
234
  st.session_state.df = None
235
  st.session_state.company_name = None
236
+ st.session_state.original_data = None
237
  st.rerun()
238
 
239
  st.divider()
 
241
  # Hämta alla områden för detta företag
242
  company_areas = get_company_areas(df, org_nr)
243
 
244
+ st.markdown(f"### 📊 Dina områden ({len(company_areas)} st)")
245
+ st.caption("💡 Klicka i valfri cell för att editera. Ändringar sparas automatiskt.")
246
 
247
+ # Visa information om vilka fält som är editerbara
248
+ with st.expander("ℹ️ Information om fält", expanded=False):
249
+ col_info1, col_info2 = st.columns(2)
250
+ with col_info1:
251
+ st.markdown("**🔒 Skrivskyddade fält:**")
252
+ for col in READONLY_COLUMNS:
253
+ if col in company_areas.columns:
254
+ st.markdown(f"- {col}")
255
+ with col_info2:
256
+ st.markdown("**✏️ Editerbara fält:**")
257
+ editable = [col for col in company_areas.columns if col not in READONLY_COLUMNS]
258
+ for col in editable:
259
+ st.markdown(f"- {col}")
260
 
261
+ st.divider()
262
+
263
+ # Skapa kolumnkonfiguration
264
+ column_config = get_column_config(company_areas)
265
+
266
+ # Visa editbar tabell
267
+ edited_data = st.data_editor(
268
+ company_areas,
269
+ column_config=column_config,
270
+ use_container_width=True,
271
+ num_rows="fixed",
272
+ hide_index=True,
273
+ key="data_editor"
274
+ )
275
+
276
+ # Detektera ändringar och spara automatiskt
277
+ if st.session_state.original_data is not None:
278
+ # Jämför edited_data med original_data
279
+ changes_detected = False
280
+ changes_list = []
281
+
282
+ for idx, (orig_row, edit_row) in enumerate(zip(
283
+ st.session_state.original_data.itertuples(index=True),
284
+ edited_data.itertuples(index=True)
285
+ )):
286
+ orig_idx = orig_row.Index
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
287
 
288
+ for col in company_areas.columns:
289
+ if col not in READONLY_COLUMNS:
290
+ orig_val = str(getattr(orig_row, col.replace(' ', '_').replace('-', '_'), ''))
291
+ edit_val = str(getattr(edit_row, col.replace(' ', '_').replace('-', '_'), ''))
 
 
 
 
 
 
292
 
293
+ if orig_val != edit_val:
294
+ changes_detected = True
295
+ changes_list.append({
296
+ 'row_idx': orig_idx,
297
+ 'column': col,
298
+ 'old_value': orig_val,
299
+ 'new_value': edit_val
300
+ })
301
+
302
+ # Om ändringar upptäckts, spara automatiskt
303
+ if changes_detected:
304
+ st.divider()
305
 
306
+ with st.container():
307
+ st.markdown(f"### 💾 Sparar {len(changes_list)} ändringar...")
 
308
 
309
+ progress_bar = st.progress(0)
310
+ success_count = 0
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
311
 
312
+ for i, change in enumerate(changes_list):
313
+ if update_cell(sheet, change['row_idx'], change['column'], change['new_value'], df):
314
+ success_count += 1
315
+ progress_bar.progress((i + 1) / len(changes_list))
316
+ time.sleep(0.1) # Liten fördröjning för att inte överbelasta API
317
+
318
+ if success_count == len(changes_list):
319
+ st.markdown('<div class="success-banner">✅ Alla ändringar sparade!</div>', unsafe_allow_html=True)
320
+ time.sleep(1)
321
+
322
+ # Uppdatera original data
323
+ st.cache_resource.clear()
324
+ new_df = get_sheet_data(sheet)
325
+ if new_df is not None:
326
+ st.session_state.df = new_df
327
+ company_areas = get_company_areas(new_df, org_nr)
328
+ st.session_state.original_data = company_areas.copy()
329
+ st.rerun()
330
+ else:
331
+ st.error(f"⚠️ Kunde bara spara {success_count} av {len(changes_list)} ändringar")
332
 
333
  # Footer
334
  st.divider()
335
+ st.caption(f"🔒 Säker åtkomst • Inloggad som {company_name} • {len(company_areas)} områden")
336
+ st.caption("💡 Tips: Använd Ctrl+F (Cmd+F på Mac) för att söka i tabellen")