k96beni commited on
Commit
335e4cb
·
verified ·
1 Parent(s): 85320b0

Upload 10 files

Browse files
Files changed (10) hide show
  1. .gitignore +52 -0
  2. GITHUB_FILER.md +71 -0
  3. HUGGINGFACE_SETUP.md +211 -0
  4. MAPPSTRUKTUR.md +41 -0
  5. PROJEKTSTRUKTUR.md +96 -0
  6. README.md +251 -13
  7. START_HÄR.md +154 -0
  8. app.py +311 -0
  9. requirements.txt +4 -0
  10. secrets.toml.template +17 -0
.gitignore ADDED
@@ -0,0 +1,52 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Secrets - VIKTIGT: Lägg aldrig till secrets i Git!
2
+ .streamlit/secrets.toml
3
+ *.json
4
+ secrets/
5
+ credentials/
6
+
7
+ # Virtual environments
8
+ venv/
9
+ env/
10
+ ENV/
11
+ .venv
12
+
13
+ # Python
14
+ __pycache__/
15
+ *.py[cod]
16
+ *$py.class
17
+ *.so
18
+ .Python
19
+ build/
20
+ develop-eggs/
21
+ dist/
22
+ downloads/
23
+ eggs/
24
+ .eggs/
25
+ lib/
26
+ lib64/
27
+ parts/
28
+ sdist/
29
+ var/
30
+ wheels/
31
+ *.egg-info/
32
+ .installed.cfg
33
+ *.egg
34
+
35
+ # IDE
36
+ .vscode/
37
+ .idea/
38
+ *.swp
39
+ *.swo
40
+ *~
41
+ .DS_Store
42
+
43
+ # Logs
44
+ *.log
45
+
46
+ # Testing
47
+ .pytest_cache/
48
+ .coverage
49
+ htmlcov/
50
+
51
+ # Streamlit
52
+ .streamlit/config.toml
GITHUB_FILER.md ADDED
@@ -0,0 +1,71 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # 📦 FILER FÖR GITHUB REPO
2
+
3
+ ## ✅ Dessa 8 filer ska läggas i ditt GitHub repo:
4
+
5
+ 1. **app.py** - Huvudapplikationen (11 KB)
6
+ 2. **requirements.txt** - Python dependencies (68 bytes)
7
+ 3. **.gitignore** - Skyddar dina secrets (476 bytes)
8
+ 4. **README.md** - Fullständig dokumentation (6.6 KB)
9
+ 5. **HUGGINGFACE_SETUP.md** - Guide för Hugging Face (5.8 KB)
10
+ 6. **PROJEKTSTRUKTUR.md** - Översikt (2.3 KB)
11
+ 7. **START_HÄR.md** - Snabbstart (4.5 KB)
12
+ 8. **secrets.toml.template** - Mall för secrets (757 bytes)
13
+
14
+ **Total storlek:** ~31 KB
15
+
16
+ ---
17
+
18
+ ## 🚀 Snabbkommando för Git:
19
+
20
+ ```bash
21
+ # I din projektmapp:
22
+ git init
23
+ git add app.py requirements.txt .gitignore README.md HUGGINGFACE_SETUP.md PROJEKTSTRUKTUR.md START_HÄR.md secrets.toml.template
24
+ git commit -m "Initial commit: Områdesdata Manager"
25
+ git remote add origin https://github.com/DITT-ANVÄNDARNAMN/omradesdata-manager.git
26
+ git branch -M main
27
+ git push -u origin main
28
+ ```
29
+
30
+ ---
31
+
32
+ ## ❌ Lägg ALDRIG till dessa:
33
+
34
+ - `.streamlit/secrets.toml` (dina riktiga secrets)
35
+ - `*.json` (Service Account nycklar)
36
+ - `venv/` (virtuell miljö)
37
+ - `__pycache__/` (Python cache)
38
+
39
+ **.gitignore skyddar dig automatiskt!**
40
+
41
+ ---
42
+
43
+ ## 📋 Verifiering:
44
+
45
+ Efter push till GitHub, kontrollera att:
46
+
47
+ ✅ Alla 8 filer är synliga i ditt repo
48
+ ✅ .gitignore fungerar (inga secrets synliga)
49
+ ✅ README.md visas på repots startsida
50
+
51
+ ---
52
+
53
+ ## 🔗 Nästa steg efter GitHub push:
54
+
55
+ 1. **Streamlit Cloud:** share.streamlit.io
56
+ - Välj ditt repo
57
+ - Lägg till secrets i web-interfacet
58
+
59
+ 2. **Hugging Face:** huggingface.co/spaces
60
+ - Skapa Space med Streamlit SDK
61
+ - Lägg till GOOGLE_CREDENTIALS och SHEET_ID i secrets
62
+
63
+ ---
64
+
65
+ ## 💡 Tips:
66
+
67
+ - Gör repot **Private** om data är känslig
68
+ - Lägg till en LICENSE-fil om du vill
69
+ - Uppdatera README.md med dina egna anteckningar
70
+
71
+ Lycka till! 🎉
HUGGINGFACE_SETUP.md ADDED
@@ -0,0 +1,211 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # 🚀 Snabbstart: Hugging Face Spaces
2
+
3
+ ## Steg-för-steg guide för att deploya på Hugging Face
4
+
5
+ ### 1. Förbered Google Cloud (om inte redan gjort)
6
+
7
+ Se huvudinstruktioner i README.md för att:
8
+ - Skapa Google Cloud project
9
+ - Aktivera Google Sheets API och Google Drive API
10
+ - Skapa Service Account och ladda ner JSON-nyckel
11
+ - Dela ditt Google Sheet med service account-emailen
12
+
13
+ ### 2. Skapa Hugging Face Space
14
+
15
+ 1. Gå till [huggingface.co/spaces](https://huggingface.co/spaces)
16
+ 2. Klicka **"Create new Space"**
17
+ 3. Fyll i:
18
+ - **Owner**: Ditt användarnamn
19
+ - **Space name**: `omradesdata-manager` (eller valfritt namn)
20
+ - **License**: Apache 2.0 (eller valfri)
21
+ - **Select the Space SDK**: **Streamlit**
22
+ - **Space hardware**: CPU basic (gratis)
23
+ 4. Klicka **"Create Space"**
24
+
25
+ ### 3. Ladda upp filer
26
+
27
+ Du har två alternativ:
28
+
29
+ #### Alternativ A: Via Web Interface
30
+
31
+ 1. I ditt nya Space, klicka **"Files"**
32
+ 2. Klicka **"Add file"** → **"Upload files"**
33
+ 3. Ladda upp:
34
+ - `app.py`
35
+ - `requirements.txt`
36
+ 4. Klicka **"Commit changes"**
37
+
38
+ #### Alternativ B: Via Git (rekommenderas för framtida uppdateringar)
39
+
40
+ ```bash
41
+ # Klona ditt Space
42
+ git clone https://huggingface.co/spaces/DITT-ANVÄNDARNAMN/omradesdata-manager
43
+ cd omradesdata-manager
44
+
45
+ # Kopiera filer
46
+ cp /sökväg/till/app.py .
47
+ cp /sökväg/till/requirements.txt .
48
+
49
+ # Commit och push
50
+ git add app.py requirements.txt
51
+ git commit -m "Initial commit"
52
+ git push
53
+ ```
54
+
55
+ ### 4. Konfigurera Secrets
56
+
57
+ Detta är det **viktigaste steget**!
58
+
59
+ 1. Gå till ditt Space
60
+ 2. Klicka **"Settings"** (kugghjulsikonen)
61
+ 3. Scrolla ner till **"Repository secrets"**
62
+ 4. Klicka **"New secret"**
63
+
64
+ #### Lägg till Sheet ID:
65
+
66
+ - **Name**: `SHEET_ID`
67
+ - **Value**: `DIN_GOOGLE_SHEET_ID` (från URL:en)
68
+ - Klicka **"Save"**
69
+
70
+ #### Lägg till Google Credentials:
71
+
72
+ - **Name**: `GOOGLE_CREDENTIALS`
73
+ - **Value**: Hela innehållet från din JSON-nyckel (kopiera allt!)
74
+ - Klicka **"Save"**
75
+
76
+ ### 5. Uppdatera app.py för Hugging Face
77
+
78
+ Lägg till dessa rader i början av `app.py` (efter imports):
79
+
80
+ ```python
81
+ import json
82
+ import os
83
+
84
+ # För Hugging Face Spaces - läs secrets från environment
85
+ if 'GOOGLE_CREDENTIALS' in os.environ:
86
+ # Vi är på Hugging Face
87
+ google_creds = json.loads(os.environ['GOOGLE_CREDENTIALS'])
88
+ sheet_id = os.environ['SHEET_ID']
89
+
90
+ # Skapa fake secrets-struktur för kompatibilitet
91
+ if 'secrets' not in st.__dict__ or not hasattr(st, 'secrets'):
92
+ class Secrets:
93
+ def __init__(self):
94
+ self.data = {
95
+ 'gcp_service_account': google_creds,
96
+ 'sheet_id': sheet_id
97
+ }
98
+ def __getitem__(self, key):
99
+ return self.data[key]
100
+
101
+ st.secrets = Secrets()
102
+ ```
103
+
104
+ **ELLER** använd denna uppdaterade `get_google_sheet()` funktion:
105
+
106
+ ```python
107
+ @st.cache_resource
108
+ def get_google_sheet():
109
+ """Anslut till Google Sheets"""
110
+ try:
111
+ # Försök först med Streamlit secrets (lokal utveckling)
112
+ if hasattr(st, 'secrets') and 'gcp_service_account' in st.secrets:
113
+ creds = Credentials.from_service_account_info(
114
+ st.secrets["gcp_service_account"],
115
+ scopes=SCOPES
116
+ )
117
+ sheet_id = st.secrets["sheet_id"]
118
+ # Annars använd environment variables (Hugging Face)
119
+ elif 'GOOGLE_CREDENTIALS' in os.environ:
120
+ google_creds = json.loads(os.environ['GOOGLE_CREDENTIALS'])
121
+ creds = Credentials.from_service_account_info(
122
+ google_creds,
123
+ scopes=SCOPES
124
+ )
125
+ sheet_id = os.environ['SHEET_ID']
126
+ else:
127
+ st.error("❌ Inga credentials hittades!")
128
+ return None
129
+
130
+ client = gspread.authorize(creds)
131
+ sheet = client.open_by_key(sheet_id).sheet1
132
+ return sheet
133
+ except Exception as e:
134
+ st.error(f"❌ Kunde inte ansluta till Google Sheets: {e}")
135
+ return None
136
+ ```
137
+
138
+ ### 6. Testa deployment
139
+
140
+ 1. Space kommer automatiskt att bygga och deploya
141
+ 2. Vänta 1-2 minuter
142
+ 3. När status visar **"Running"**, klicka på din Space-URL
143
+ 4. Testa att logga in med ett lösenord från ditt sheet!
144
+
145
+ ### 7. Felsökning
146
+
147
+ #### "Kunde inte ansluta till Google Sheets"
148
+
149
+ **Kontrollera:**
150
+ - [ ] GOOGLE_CREDENTIALS är korrekt JSON (hela filen)
151
+ - [ ] SHEET_ID är rätt (från Google Sheets URL)
152
+ - [ ] Service account har tillgång till sheetet
153
+ - [ ] API:erna är aktiverade i Google Cloud
154
+
155
+ #### "No module named 'gspread'"
156
+
157
+ **Lösning:**
158
+ - Kontrollera att `requirements.txt` är uppladdad
159
+ - Rebuild Space (Settings → Factory reboot)
160
+
161
+ #### Space kraschar
162
+
163
+ **Kontrollera:**
164
+ - Logs i Space (klicka på "Logs")
165
+ - Syntax-fel i app.py
166
+ - Secrets är korrekt formaterade
167
+
168
+ ### 8. Gör din Space privat (valfritt)
169
+
170
+ Om du vill ha en privat app:
171
+
172
+ 1. Gå till **Settings**
173
+ 2. Under **"Space visibility"**
174
+ 3. Välj **"Private"**
175
+ 4. Klicka **"Save"**
176
+
177
+ Nu kan bara du (och personer du delar länken med) se appen!
178
+
179
+ ### 9. Uppdatera din app
180
+
181
+ För att göra ändringar i framtiden:
182
+
183
+ ```bash
184
+ cd omradesdata-manager
185
+ # Gör dina ändringar i app.py
186
+ git add app.py
187
+ git commit -m "Uppdaterad funktion X"
188
+ git push
189
+ ```
190
+
191
+ Space kommer automatiskt att rebuilda!
192
+
193
+ ## 🎉 Klart!
194
+
195
+ Din app körs nu på Hugging Face Spaces! Dela länken med dina användare:
196
+
197
+ ```
198
+ https://huggingface.co/spaces/DITT-ANVÄNDARNAMN/omradesdata-manager
199
+ ```
200
+
201
+ ## 💡 Pro Tips
202
+
203
+ 1. **Snabbare builds**: Pin exakta versioner i requirements.txt
204
+ 2. **Bättre prestanda**: Använd st.cache_resource för datahämtning
205
+ 3. **Monitoring**: Kolla Logs regelbundet för fel
206
+ 4. **Backups**: Gör regelbundna backups av ditt Google Sheet
207
+
208
+ ## 📞 Behöver du hjälp?
209
+
210
+ - Hugging Face Community: [huggingface.co/discussions](https://huggingface.co/discussions)
211
+ - Streamlit Forum: [discuss.streamlit.io](https://discuss.streamlit.io)
MAPPSTRUKTUR.md ADDED
@@ -0,0 +1,41 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # 📂 Mappstruktur för ditt projekt
2
+
3
+ ```
4
+ omradesdata-manager/
5
+
6
+ ├── 📄 app.py # Huvudapplikationen
7
+ ├── 📄 requirements.txt # Python dependencies
8
+ ├── 📄 .gitignore # Skyddar secrets från Git
9
+ ├── 📄 README.md # Fullständig dokumentation
10
+ ├── 📄 HUGGINGFACE_SETUP.md # Guide för Hugging Face
11
+ ├── 📄 PROJEKTSTRUKTUR.md # Översikt över projektet
12
+ ├── 📄 START_HÄR.md # Snabbstartguide
13
+ ├── 📄 secrets.toml.template # Mall för secrets (utan riktiga värden)
14
+
15
+ └── 📁 .streamlit/ # ⚠️ SKAPA DENNA LOKALT (lägg INTE i Git!)
16
+ └── 📄 secrets.toml # Dina riktiga secrets (endast lokalt)
17
+ ```
18
+
19
+ ## 📦 Vad som ska i GitHub:
20
+
21
+ Allt UTOM `.streamlit/` mappen!
22
+
23
+ ## 🏠 Vad som ska finnas lokalt:
24
+
25
+ Allt ovan + `.streamlit/secrets.toml` (för lokal testning)
26
+
27
+ ## ☁️ Deployment-secrets:
28
+
29
+ - **Streamlit Cloud**: Lägg in manuellt i web-interfacet
30
+ - **Hugging Face**: Lägg in i Repository secrets
31
+
32
+ ---
33
+
34
+ ## 🎯 Snabb setup:
35
+
36
+ 1. **Ladda ner** alla filer från ZIP-filen
37
+ 2. **Packa upp** i en mapp (t.ex. `omradesdata-manager`)
38
+ 3. **Skapa** `.streamlit/secrets.toml` lokalt (kopiera från template)
39
+ 4. **Testa** lokalt: `streamlit run app.py`
40
+ 5. **Pusha** till GitHub (utan .streamlit/)
41
+ 6. **Deploya** på Streamlit Cloud eller Hugging Face
PROJEKTSTRUKTUR.md ADDED
@@ -0,0 +1,96 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # 📁 Projektstruktur
2
+
3
+ ```
4
+ omradesdata-manager/
5
+
6
+ ├── app.py # Huvudapplikationen
7
+ ├── requirements.txt # Python-dependencies
8
+ ├── .gitignore # Git ignore-fil
9
+ ├── README.md # Huvuddokumentation
10
+ ├── HUGGINGFACE_SETUP.md # Guide för Hugging Face deployment
11
+ ├── secrets.toml.template # Mall för secrets-fil
12
+
13
+ └── .streamlit/ # Skapas av dig
14
+ └── secrets.toml # Dina secrets (lägg INTE i Git!)
15
+ ```
16
+
17
+ ## 📝 Filbeskrivningar
18
+
19
+ ### app.py
20
+ Huvudapplikationen med all funktionalitet:
21
+ - Lösenordsautentisering
22
+ - Visa områdesdata
23
+ - Editera tillåtna fält
24
+ - Spara ändringar till Google Sheets
25
+
26
+ ### requirements.txt
27
+ Lista över Python-paket som behövs:
28
+ - streamlit
29
+ - gspread
30
+ - google-auth
31
+ - pandas
32
+
33
+ ### .gitignore
34
+ Skyddar känsliga filer från att hamna i Git:
35
+ - Secrets och credentials
36
+ - Virtuella miljöer
37
+ - Cache-filer
38
+
39
+ ### README.md
40
+ Komplett dokumentation:
41
+ - Installation
42
+ - Google Cloud setup
43
+ - Deployment-instruktioner
44
+ - Felsökning
45
+
46
+ ### HUGGINGFACE_SETUP.md
47
+ Specifik guide för deployment på Hugging Face Spaces
48
+
49
+ ### secrets.toml.template
50
+ Mall för att skapa din egen secrets-fil
51
+
52
+ ## 🚀 Snabbstart
53
+
54
+ 1. **Lokalt:**
55
+ ```bash
56
+ pip install -r requirements.txt
57
+ # Skapa .streamlit/secrets.toml enligt mall
58
+ streamlit run app.py
59
+ ```
60
+
61
+ 2. **Hugging Face Spaces:**
62
+ - Ladda upp app.py och requirements.txt
63
+ - Konfigurera secrets enligt HUGGINGFACE_SETUP.md
64
+
65
+ ## 🔑 Secrets-konfiguration
66
+
67
+ ### Lokal utveckling (.streamlit/secrets.toml)
68
+ ```toml
69
+ sheet_id = "DIN_GOOGLE_SHEET_ID"
70
+
71
+ [gcp_service_account]
72
+ # Din Google Service Account JSON-data här
73
+ ```
74
+
75
+ ### Hugging Face Spaces (Environment Variables)
76
+ ```
77
+ SHEET_ID = "DIN_GOOGLE_SHEET_ID"
78
+ GOOGLE_CREDENTIALS = {hela JSON-objektet}
79
+ ```
80
+
81
+ ## 💡 Nästa steg
82
+
83
+ 1. Läs README.md för fullständig setup-guide
84
+ 2. Sätt upp Google Cloud och Service Account
85
+ 3. Konfigurera secrets
86
+ 4. Testa lokalt först
87
+ 5. Deploya till Hugging Face Spaces
88
+
89
+ ## 🔒 Säkerhet
90
+
91
+ **VIKTIGT:** Lägg ALDRIG till dessa filer i Git:
92
+ - `.streamlit/secrets.toml`
93
+ - `*.json` (Service Account-nycklar)
94
+ - Virtuella miljöer (`venv/`, `env/`)
95
+
96
+ Använd alltid `.gitignore`!
README.md CHANGED
@@ -1,13 +1,251 @@
1
- ---
2
- title: Area Updater
3
- emoji: 😻
4
- colorFrom: blue
5
- colorTo: indigo
6
- sdk: gradio
7
- sdk_version: 5.49.1
8
- app_file: app.py
9
- pinned: false
10
- short_description: Områdes uppdaterare för ChargeNode
11
- ---
12
-
13
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # 🏢 Områdesdata Manager
2
+
3
+ En säker Streamlit-applikation för att hantera områdesdata från Google Sheets med lösenordsskydd.
4
+
5
+ ## ✨ Funktioner
6
+
7
+ - 🔐 **Lösenordsskydd per område** - Varje område har sitt eget unika lösenord
8
+ - 🏢 **Företagsvy** - Se alla områden för ditt företag efter inloggning
9
+ - ✏️ **Selektiv redigering** - Vissa fält är skrivskyddade (org.nr, företagsnamn, etc.)
10
+ - 💾 **Direkt synkronisering** - Ändringar sparas direkt till Google Sheets
11
+ - 🔄 **Realtidsuppdatering** - Hämta senaste data från arket
12
+ - 📱 **Responsiv design** - Fungerar på desktop och mobil
13
+
14
+ ## 📋 Förutsättningar
15
+
16
+ - Python 3.8 eller senare
17
+ - Ett Google Cloud-konto
18
+ - Google Sheets med data
19
+
20
+ ## 🚀 Installation och Setup
21
+
22
+ ### Steg 1: Klona eller ladda ner projektet
23
+
24
+ ```bash
25
+ # Skapa en ny mapp för projektet
26
+ mkdir omradesdata-manager
27
+ cd omradesdata-manager
28
+
29
+ # Lägg till filerna (app.py, requirements.txt)
30
+ ```
31
+
32
+ ### Steg 2: Skapa virtuell miljö (rekommenderas)
33
+
34
+ ```bash
35
+ # Skapa virtuell miljö
36
+ python -m venv venv
37
+
38
+ # Aktivera virtuell miljö
39
+ # På Windows:
40
+ venv\Scripts\activate
41
+ # På Mac/Linux:
42
+ source venv/bin/activate
43
+ ```
44
+
45
+ ### Steg 3: Installera dependencies
46
+
47
+ ```bash
48
+ pip install -r requirements.txt
49
+ ```
50
+
51
+ ### Steg 4: Google Cloud Setup
52
+
53
+ #### 4.1 Skapa Google Cloud Project
54
+
55
+ 1. Gå till [Google Cloud Console](https://console.cloud.google.com/)
56
+ 2. Klicka på "Select a project" → "New Project"
57
+ 3. Namnge projektet (t.ex. "omradesdata-manager")
58
+ 4. Klicka "Create"
59
+
60
+ #### 4.2 Aktivera API:er
61
+
62
+ 1. I menyn, gå till "APIs & Services" → "Library"
63
+ 2. Sök och aktivera:
64
+ - **Google Sheets API**
65
+ - **Google Drive API**
66
+
67
+ #### 4.3 Skapa Service Account
68
+
69
+ 1. Gå till "APIs & Services" → "Credentials"
70
+ 2. Klicka "Create Credentials" → "Service Account"
71
+ 3. Namnge service account (t.ex. "sheets-manager")
72
+ 4. Klicka "Create and Continue"
73
+ 5. Välj roll: **Editor** (eller "Basic" → "Editor")
74
+ 6. Klicka "Done"
75
+
76
+ #### 4.4 Generera JSON-nyckel
77
+
78
+ 1. I listan över service accounts, klicka på den du nyss skapade
79
+ 2. Gå till fliken "Keys"
80
+ 3. Klicka "Add Key" → "Create new key"
81
+ 4. Välj **JSON**
82
+ 5. Klicka "Create"
83
+ 6. En JSON-fil laddas ner automatiskt - **spara den säkert!**
84
+
85
+ #### 4.5 Dela Google Sheet med Service Account
86
+
87
+ 1. Öppna din JSON-nyckel och hitta värdet för `client_email`
88
+ - Det ser ut som: `sheets-manager@ditt-project.iam.gserviceaccount.com`
89
+ 2. Öppna ditt Google Sheet
90
+ 3. Klicka "Share" (Dela)
91
+ 4. Lägg till service account-emailen
92
+ 5. Ge den **Editor**-behörighet
93
+ 6. Klicka "Send"
94
+
95
+ ### Steg 5: Konfigurera Secrets
96
+
97
+ #### 5.1 Hitta ditt Sheet ID
98
+
99
+ Öppna ditt Google Sheet och titta på URL:en:
100
+ ```
101
+ https://docs.google.com/spreadsheets/d/DETTA_ÄR_DITT_SHEET_ID/edit
102
+ ```
103
+
104
+ #### 5.2 Skapa secrets-fil
105
+
106
+ ```bash
107
+ # Skapa .streamlit-mapp
108
+ mkdir .streamlit
109
+
110
+ # Kopiera mall
111
+ cp secrets.toml.template .streamlit/secrets.toml
112
+ ```
113
+
114
+ #### 5.3 Fyll i secrets.toml
115
+
116
+ Öppna `.streamlit/secrets.toml` och:
117
+
118
+ 1. Ersätt `DIN_GOOGLE_SHEET_ID_HÄR` med ditt Sheet ID
119
+ 2. Öppna din JSON-nyckel och kopiera alla värden till motsvarande fält
120
+
121
+ Exempel:
122
+ ```toml
123
+ sheet_id = "1ABC123xyz789..."
124
+
125
+ [gcp_service_account]
126
+ type = "service_account"
127
+ project_id = "omradesdata-manager-123"
128
+ private_key_id = "abc123..."
129
+ private_key = "-----BEGIN PRIVATE KEY-----\nMIIE...\n-----END PRIVATE KEY-----\n"
130
+ client_email = "sheets-manager@omradesdata-manager-123.iam.gserviceaccount.com"
131
+ # ... etc
132
+ ```
133
+
134
+ **VIKTIGT:** Lägg till `.streamlit/secrets.toml` i `.gitignore`!
135
+
136
+ ### Steg 6: Kör applikationen lokalt
137
+
138
+ ```bash
139
+ streamlit run app.py
140
+ ```
141
+
142
+ Appen öppnas automatiskt i din webbläsare på `http://localhost:8501`
143
+
144
+ ## 🌐 Deployment
145
+
146
+ ### Alternativ 1: Streamlit Community Cloud (Gratis)
147
+
148
+ 1. Pusha din kod till GitHub (utan secrets!)
149
+ 2. Gå till [share.streamlit.io](https://share.streamlit.io)
150
+ 3. Klicka "New app"
151
+ 4. Välj ditt repository
152
+ 5. I "Advanced settings" → "Secrets", klistra in innehållet från din `secrets.toml`
153
+ 6. Klicka "Deploy"
154
+
155
+ ### Alternativ 2: Hugging Face Spaces
156
+
157
+ 1. Skapa ett nytt Space på [Hugging Face](https://huggingface.co/spaces)
158
+ 2. Välj **Streamlit** som SDK
159
+ 3. Ladda upp `app.py` och `requirements.txt`
160
+ 4. I Space Settings → Repository secrets:
161
+ - Lägg till variabel `STREAMLIT_SECRETS`
162
+ - Klistra in hela innehållet från din `secrets.toml`
163
+ 5. Space kommer automatiskt att deploya
164
+
165
+ ## 🔧 Anpassning
166
+
167
+ ### Ändra skrivskyddade kolumner
168
+
169
+ I `app.py`, hitta variabeln `READONLY_COLUMNS` och ändra efter behov:
170
+
171
+ ```python
172
+ READONLY_COLUMNS = [
173
+ 'Tidstämpel',
174
+ 'Organisationsnummer',
175
+ 'Lösenord',
176
+ 'Account',
177
+ # Lägg till fler kolumner här...
178
+ ]
179
+ ```
180
+
181
+ ### Ändra layout
182
+
183
+ Koden använder Streamlit's `st.columns()` för layout. Justera kolumnbredder:
184
+
185
+ ```python
186
+ # Exempel: Ändra från [1, 2, 1] till [1, 3, 1]
187
+ col1, col2, col3 = st.columns([1, 3, 1])
188
+ ```
189
+
190
+ ## 📊 Datastruktur
191
+
192
+ Appen förväntar sig följande kolumner i Google Sheet:
193
+
194
+ | Kolumn | Beskrivning | Editerbar |
195
+ |--------|-------------|-----------|
196
+ | Tidstämpel | När området skapades | Nej |
197
+ | Organisationsnummer | Företagets org.nr | Nej |
198
+ | Lösenord | Unikt lösenord per område | Nej |
199
+ | Områdeskod | Identifierare för området | Nej |
200
+ | För- och efternamn | Kontaktperson | Ja |
201
+ | Telefonnummer | Kontakt telefon | Ja |
202
+ | Adress | Leveransadress | Ja |
203
+ | ... | Övriga fält | Ja |
204
+
205
+ ## 🔒 Säkerhet
206
+
207
+ - ✅ Lösenord visas aldrig i klartext
208
+ - ✅ Secrets lagras säkert (inte i kod)
209
+ - ✅ Ingen data cachas på klientsidan
210
+ - ✅ Direktkommunikation med Google Sheets API
211
+
212
+ ### Rekommenderade förbättringar:
213
+
214
+ 1. **Rate limiting** - Begränsa antal inloggningsförsök
215
+ 2. **Session timeout** - Automatisk utloggning efter inaktivitet
216
+ 3. **Audit log** - Logga alla ändringar
217
+ 4. **HTTPS** - Använd alltid HTTPS i produktion
218
+
219
+ ## ❓ Felsökning
220
+
221
+ ### Problem: "Kunde inte ansluta till Google Sheets"
222
+
223
+ **Lösning:**
224
+ 1. Kontrollera att Sheet ID är korrekt
225
+ 2. Verifiera att service account-emailen har tillgång till sheetet
226
+ 3. Dubbelkolla att API:erna är aktiverade
227
+
228
+ ### Problem: "Ogiltigt lösenord"
229
+
230
+ **Lösning:**
231
+ 1. Kontrollera att lösenordet i sheetet matchar exakt (inga extra mellanslag)
232
+ 2. Lösenord är case-sensitive
233
+ 3. Uppdatera data med 🔄-knappen
234
+
235
+ ### Problem: "Kan inte uppdatera celler"
236
+
237
+ **Lösning:**
238
+ 1. Service account behöver **Editor**-behörighet, inte bara Viewer
239
+ 2. Kontrollera att sheetet inte är skrivskyddat
240
+
241
+ ## 📝 Licens
242
+
243
+ Detta projekt är öppen källkod och fritt att använda och modifiera.
244
+
245
+ ## 🤝 Bidrag
246
+
247
+ Förslag och förbättringar är välkomna!
248
+
249
+ ## 📧 Support
250
+
251
+ För frågor eller problem, kontakta utvecklaren.
START_HÄR.md ADDED
@@ -0,0 +1,154 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # 🚀 START HÄR!
2
+
3
+ Tack för att du använder Områdesdata Manager! Här är en snabb guide för att komma igång.
4
+
5
+ ## 📦 Vad har du fått?
6
+
7
+ Du har nu alla filer du behöver för att skapa en säker webbapp för att hantera ditt Google Sheet:
8
+
9
+ ✅ **app.py** - Huvudapplikationen
10
+ ✅ **requirements.txt** - Dependencies
11
+ ✅ **README.md** - Fullständig dokumentation
12
+ ✅ **HUGGINGFACE_SETUP.md** - Guide för Hugging Face
13
+ ✅ **.gitignore** - Skyddar dina secrets
14
+ ✅ **secrets.toml.template** - Mall för konfiguration
15
+ ✅ **PROJEKTSTRUKTUR.md** - Översikt över projektet
16
+
17
+ ## 🎯 Vad gör appen?
18
+
19
+ 1. **Lösenordsskydd** - Varje område har sitt eget lösenord (kolumn C i ditt sheet)
20
+ 2. **Visa alla områden** - När användaren loggar in ser de alla områden för sitt företag
21
+ 3. **Editera data** - Vissa kolumner kan editeras, andra är skrivskyddade
22
+ 4. **Spara direkt** - Ändringar sparas direkt till Google Sheets
23
+ 5. **Säker** - Data visas bara efter korrekt lösenord
24
+
25
+ ## 🏃 Snabbstart (3 enkla val)
26
+
27
+ ### Alternativ 1: Testa lokalt (Rekommenderas först!)
28
+
29
+ ```bash
30
+ # 1. Installera dependencies
31
+ pip install -r requirements.txt
32
+
33
+ # 2. Skapa .streamlit-mapp
34
+ mkdir .streamlit
35
+
36
+ # 3. Kopiera och fyll i secrets
37
+ cp secrets.toml.template .streamlit/secrets.toml
38
+ # Redigera .streamlit/secrets.toml med dina credentials
39
+
40
+ # 4. Kör appen
41
+ streamlit run app.py
42
+ ```
43
+
44
+ **För lokal körning behöver du:**
45
+ - Google Cloud Service Account (se README.md för guide)
46
+ - Dela ditt Google Sheet med service account-emailen
47
+ - Fyll i `.streamlit/secrets.toml` med dina credentials
48
+
49
+ ### Alternativ 2: Hugging Face Spaces (Gratis hosting!)
50
+
51
+ 1. Gå till [huggingface.co/spaces](https://huggingface.co/spaces)
52
+ 2. Skapa nytt Space med Streamlit SDK
53
+ 3. Ladda upp `app.py` och `requirements.txt`
54
+ 4. Lägg till secrets (se HUGGINGFACE_SETUP.md)
55
+
56
+ **Secrets att lägga till på Hugging Face:**
57
+ - `SHEET_ID` - Ditt Google Sheet ID
58
+ - `GOOGLE_CREDENTIALS` - Hela JSON-nyckeln från Google Cloud
59
+
60
+ ### Alternativ 3: Streamlit Community Cloud
61
+
62
+ 1. Pusha kod till GitHub (utan secrets!)
63
+ 2. Gå till [share.streamlit.io](https://share.streamlit.io)
64
+ 3. Välj ditt repo och deploya
65
+ 4. Lägg till secrets i Advanced Settings
66
+
67
+ ## 📚 Detaljerade guider
68
+
69
+ - **Fullständig setup-guide**: Läs `README.md`
70
+ - **Hugging Face deployment**: Läs `HUGGINGFACE_SETUP.md`
71
+ - **Projektöversikt**: Läs `PROJEKTSTRUKTUR.md`
72
+
73
+ ## 🔧 Google Cloud Setup (Måste göras först!)
74
+
75
+ Innan du kan köra appen behöver du:
76
+
77
+ 1. **Skapa Google Cloud Project**
78
+ - Gå till [console.cloud.google.com](https://console.cloud.google.com)
79
+ - Skapa nytt projekt
80
+
81
+ 2. **Aktivera API:er**
82
+ - Google Sheets API
83
+ - Google Drive API
84
+
85
+ 3. **Skapa Service Account**
86
+ - Gå till "APIs & Services" → "Credentials"
87
+ - Skapa Service Account
88
+ - Ladda ner JSON-nyckel
89
+
90
+ 4. **Dela Google Sheet**
91
+ - Kopiera service account-emailen från JSON-filen
92
+ - Dela ditt Google Sheet med denna email (ge Editor-behörighet)
93
+
94
+ **Fullständig guide finns i README.md!**
95
+
96
+ ## 🎨 Anpassa appen
97
+
98
+ ### Ändra vilka kolumner som får editeras
99
+
100
+ I `app.py`, hitta `READONLY_COLUMNS` och ändra:
101
+
102
+ ```python
103
+ READONLY_COLUMNS = [
104
+ 'Tidstämpel',
105
+ 'Organisationsnummer',
106
+ 'Lösenord',
107
+ 'Account',
108
+ # Lägg till fler här...
109
+ ]
110
+ ```
111
+
112
+ ### Ändra styling
113
+
114
+ Koden använder Streamlit och kan anpassas efter dina behov. Se dokumentationen i app.py.
115
+
116
+ ## ❓ Behöver du hjälp?
117
+
118
+ ### Vanliga problem:
119
+
120
+ **"Kunde inte ansluta till Google Sheets"**
121
+ → Kontrollera att Sheet ID är korrekt och att API:erna är aktiverade
122
+
123
+ **"Ogiltigt lösenord"**
124
+ → Lösenord är case-sensitive och får inte ha extra mellanslag
125
+
126
+ **"Kan inte uppdatera celler"**
127
+ → Service account behöver Editor-behörighet (inte bara Viewer)
128
+
129
+ ### Mer hjälp:
130
+
131
+ - Läs felsökningsavsnittet i README.md
132
+ - Kolla logs om du kör på Hugging Face
133
+ - Testa lokalt först för enklare debugging
134
+
135
+ ## 🔒 Säkerhetsregler
136
+
137
+ **VIKTIGT! Lägg ALDRIG till dessa filer i Git:**
138
+ - `.streamlit/secrets.toml`
139
+ - `*.json` (Service Account-nycklar)
140
+ - Din Google Sheet ID (om den är känslig)
141
+
142
+ `.gitignore` är redan konfigurerad för att skydda dig!
143
+
144
+ ## 🎉 Lycka till!
145
+
146
+ Du har nu allt du behöver för att skapa en säker webbapp för att hantera ditt Google Sheet!
147
+
148
+ **Rekommenderad ordning:**
149
+ 1. Läs README.md för fullständig förståelse
150
+ 2. Sätt upp Google Cloud (se guide i README.md)
151
+ 3. Testa lokalt först
152
+ 4. Deploya till Hugging Face eller Streamlit Cloud
153
+
154
+ **Frågor?** All dokumentation finns i filerna! ✨
app.py ADDED
@@ -0,0 +1,311 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import streamlit as st
2
+ import gspread
3
+ from google.oauth2.service_account import Credentials
4
+ import pandas as pd
5
+ from datetime import datetime
6
+ import json
7
+ import os
8
+
9
+ # Konfigurera sidan
10
+ st.set_page_config(
11
+ page_title="Områdesdata Manager",
12
+ layout="wide",
13
+ initial_sidebar_state="collapsed"
14
+ )
15
+
16
+ # Google Sheets setup
17
+ SCOPES = [
18
+ 'https://www.googleapis.com/auth/spreadsheets',
19
+ 'https://www.googleapis.com/auth/drive'
20
+ ]
21
+
22
+ # Definiera vilka kolumner som INTE får editeras (0-indexerade)
23
+ READONLY_COLUMNS = [
24
+ 'Tidstämpel',
25
+ 'Organisationsnummer',
26
+ 'Lösenord',
27
+ 'Account', # Företagsnamn
28
+ 'Location Name',
29
+ 'Location - Address',
30
+ 'Long',
31
+ 'Lat',
32
+ 'Location - City'
33
+ ]
34
+
35
+ @st.cache_resource
36
+ def get_google_sheet():
37
+ """Anslut till Google Sheets - fungerar både lokalt och på Hugging Face"""
38
+ try:
39
+ # Försök först med Streamlit secrets (lokal utveckling)
40
+ if hasattr(st, 'secrets') and 'gcp_service_account' in st.secrets:
41
+ creds = Credentials.from_service_account_info(
42
+ st.secrets["gcp_service_account"],
43
+ scopes=SCOPES
44
+ )
45
+ sheet_id = st.secrets["sheet_id"]
46
+ # Annars använd environment variables (Hugging Face Spaces)
47
+ elif 'GOOGLE_CREDENTIALS' in os.environ and 'SHEET_ID' in os.environ:
48
+ google_creds = json.loads(os.environ['GOOGLE_CREDENTIALS'])
49
+ creds = Credentials.from_service_account_info(
50
+ google_creds,
51
+ scopes=SCOPES
52
+ )
53
+ sheet_id = os.environ['SHEET_ID']
54
+ else:
55
+ st.error("❌ Inga credentials hittades! Kontrollera att secrets är konfigurerade.")
56
+ st.info("💡 Lokal utveckling: Skapa .streamlit/secrets.toml\n\n💡 Hugging Face: Lägg till GOOGLE_CREDENTIALS och SHEET_ID i Repository secrets")
57
+ return None
58
+
59
+ client = gspread.authorize(creds)
60
+ sheet = client.open_by_key(sheet_id).sheet1
61
+ return sheet
62
+ except Exception as e:
63
+ st.error(f"❌ Kunde inte ansluta till Google Sheets: {e}")
64
+ return None
65
+
66
+ def get_sheet_data(sheet):
67
+ """Hämta all data från sheet"""
68
+ try:
69
+ data = sheet.get_all_records()
70
+ df = pd.DataFrame(data)
71
+ return df
72
+ except Exception as e:
73
+ st.error(f"❌ Fel vid hämtning av data: {e}")
74
+ return None
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:
82
+ org_nr = match.iloc[0]['Organisationsnummer']
83
+ return org_nr
84
+ return None
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("""
110
+ <style>
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)
129
+
130
+ # Huvudapp
131
+ st.title("🏢 Områdesdata Manager")
132
+
133
+ # Session state
134
+ if 'authenticated' not in st.session_state:
135
+ st.session_state.authenticated = False
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()
142
+
143
+ if sheet is None:
144
+ st.stop()
145
+
146
+ # Inloggning
147
+ if not st.session_state.authenticated:
148
+ st.markdown("### 🔐 Logga in med ditt områdeslösenord")
149
+
150
+ col1, col2, col3 = st.columns([1, 2, 1])
151
+
152
+ with col2:
153
+ password = st.text_input(
154
+ "Ange lösenord för ditt område:",
155
+ type="password",
156
+ key="password_input",
157
+ placeholder="Ange lösenord..."
158
+ )
159
+
160
+ if st.button("🔓 Logga in", type="primary", use_container_width=True):
161
+ if password:
162
+ with st.spinner("Validerar lösenord..."):
163
+ df = get_sheet_data(sheet)
164
+
165
+ if df is not None:
166
+ org_nr = validate_password(df, password)
167
+
168
+ if org_nr is not None:
169
+ company_areas = get_company_areas(df, org_nr)
170
+ company_name = company_areas.iloc[0]['Account']
171
+
172
+ st.session_state.authenticated = True
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.reflow()
178
+ else:
179
+ st.error("❌ Ogiltigt lösenord")
180
+ else:
181
+ st.warning("⚠️ Ange ett lösenord")
182
+
183
+ st.divider()
184
+ st.info("💡 **Information:** Detta system låter dig hantera områdesinformation. Ange ditt områdeslösenord för att se och editera tillhörande data.")
185
+
186
+ else:
187
+ # Användaren är inloggad
188
+ org_nr = st.session_state.org_nr
189
+ df = st.session_state.df
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}")
197
+ st.caption(f"Org.nr: {org_nr}")
198
+
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.reflow()
205
+
206
+ with col3:
207
+ if st.button("🚪 Logga ut", use_container_width=True):
208
+ st.session_state.authenticated = False
209
+ st.session_state.org_nr = None
210
+ st.session_state.df = None
211
+ st.session_state.company_name = None
212
+ st.reflow()
213
+
214
+ st.divider()
215
+
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.reflow()
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.reflow()
308
+
309
+ # Footer
310
+ st.divider()
311
+ st.caption(f"🔒 Säker åtkomst • Inloggad som {company_name} • {len(company_areas)} områden")
requirements.txt ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ streamlit>=1.28.0
2
+ gspread>=5.11.0
3
+ google-auth>=2.23.0
4
+ pandas>=2.0.0
secrets.toml.template ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # .streamlit/secrets.toml
2
+ # LÄGG INTE TILL DENNA FIL I GIT!
3
+
4
+ sheet_id = "DIN_GOOGLE_SHEET_ID_HÄR"
5
+
6
+ [gcp_service_account]
7
+ type = "service_account"
8
+ project_id = "ditt-project-id"
9
+ private_key_id = "xxxxxxxxxxxx"
10
+ private_key = "-----BEGIN PRIVATE KEY-----\nDIN_PRIVATE_KEY_HÄR\n-----END PRIVATE KEY-----\n"
11
+ client_email = "din-service-account@ditt-project.iam.gserviceaccount.com"
12
+ client_id = "xxxxxxxxxxxxx"
13
+ auth_uri = "https://accounts.google.com/o/oauth2/auth"
14
+ token_uri = "https://oauth2.googleapis.com/token"
15
+ auth_provider_x509_cert_url = "https://www.googleapis.com/oauth2/v1/certs"
16
+ client_x509_cert_url = "https://www.googleapis.com/robot/v1/metadata/x509/din-service-account%40ditt-project.iam.gserviceaccount.com"
17
+ universe_domain = "googleapis.com"