Streamlit ile Google Drive’a Dosya Kaydetme

Oğuzhan Arı
10 min readSep 15, 2022

--

merhaba!

son birkaç haftamı streamlit ile uğraşarak geçirdim ve aslında potansiyelinin kat kat fazla olduğu kanaatindeyim. sakarya üniversitesi kariyer ve yetenek yönetimi koordinatörlüğü için hazırlamış olduğum iki farklı uygulama (birisi öğrencilerin google form üzerinden doldurmuş olduğu kişilik envanterini analiz edip kullanıcıya ve değerlendiriciye ayrı ayrı analiz sonucu gönderirken, diğeri kullanıcıların doldurmuş oldukları bir renk testinin sonucunu bir google drive klasörüne kaydediyor.) aktif olarak kullanıma hazır şekilde çalışıyor.

eğer benim gibi ileri düzeyde bir programcı değilseniz streamlit sizi birçok açıdan, çok büyük dertlerden kurtarıyor. domain, hosting, html/css ile tasarım dizayn, sayfanın responsive olması, çalışmanın güvenliği gibi birçok konuyu sizin düşünmenize gerek kalmıyor. siz sadece python ile kodunuzu yazıyorsunuz ve.. tada! karşınızda bir web sayfası var. bu sebepten ötürü streamlit gün geçtikçe daha hoşuma gitmeye başlıyor ve yapabileceklerinizin sınırı, hayal gücünüz.

peki, bugün ne yapacağız?

streamlit ile kullanıcıdan aldığımız CSV dosyası üzerinde bir işlem yapacak, bunun sonucunu bir text dosyasına basacak ve bu süreçte yapılanların log’unu tutacak, daha sonrasında bu üç dosyayı kendimiz için (hatalarımızı düzeltmek, geliştirme yapmak veya süreçleri incelemek için) drive klasörümüze kaydedeceğiz.

burada asıl amacımız, dosyaları oluşturmak ve drive’a kaydetmek olacağı için, çok basit bir CSV dosyasından (10 satır içeren, her satırda sayılar olan) veriyi alıp, alınan verilerin toplamını bir text dosyasına kaydederek kullanıcının bu dosyayı indirebilmesini sağlayacağız. yapılan işlemlerin log kayıtlarını tutarak daha sonrasında çalışma aşamalarını inceleyebileceğiz.

o zaman, başlayalım!

google drive api’ye erişmek

programımız için çok fazla kütüphaneye ihtiyacımız yok. sadece pandas (csv’yi okuyabilmek için), pydrive (google drive’a erişebilmek için) ve streamlit (tabii ki..) kullanacağız.

normal şartlarda, bir google drive api’si kullandığımızda, program her çalıştığında bizden authorization yapmamızı isteyecek. yani, bu uygulamayı deploy ettiğimizde başka kullanıcılar da kullandığında onlardan, bu uygulamaya (dosyaları drive’a kayddetmek için kullandığımız api) yetkili bir hesapla (bir gmail hesabıyla) giriş yapmalarını isteyecek. ancak biz hangi kullanıcının kullandığını bilmediğimizden ve bir yetkilendirme sürecine girmediğimizden, kendi yetkili hesabımıza ait yetkilendirme bilgileriyle uygulama arka planda, kullanıcıya hiç hissettirmeden yetkilendirme yapacak. ancak, uygulamamızı github üzerinden paylaştıktan sonra bu yetkilendirme bilgilerimizi paylaşamayız. çünkü bu bilgiler ile herhangi birisi, herhangi bir uygulamaya “biz” gibi giriş yapabilir. bunu istemiyoruz. bu sebepten ötürü streamlit ile bu authorization dosyasını da kendimiz oluşturacağız.

o zaman kodlamaya başlayalım!

ilk önce, drive’mız için api oluşturalım ve bu uygulamaya ait client_secret dosyasını edinelim. https://console.cloud.google.com/ adresine gidiyoruz. hesabımıza giriş yaptıktan sonra google drive’ı aratıyoruz.

daha sonrasında “google drive api”yi seçiyoruz.

google drive api’yi “enable” seçeneğiyle etkinleştiriyoruz.

bu sayfa bizi direkt olarak api sayfasına yönlendiriyor. bu sayfadan “create credentials”a basarak uygulamamız için doğrulama dosyası oluşturmaya başlıyoruz.

bu sayfada ilk aşamamız, hangi uygulamayı kullanacağımızı ve kullandığımız uygulamada veri kaynağının ne olacağını soruyor. user data diyerek devam ediyoruz. buradaki önemli detay ‘user consent required’ kısmı. normalde, bu uygulamayı kullandığımızda kullanıcıların giriş yapması gerekecek. ancak biz bu kısmı kullanıcı adına kendimiz yapacağız.

OAuth Consent kısmında, uygulamamıza bir isim veriyoruz, istenilen mail adreslerini veriyor ve devam ediyoruz.

scopes kısmında, uygulamamızın neye, nereye erişebileceğine izin vereceğiz. burada, uygulamamızın bize ait drive üstünde tam yetkiye sahip olmasını istiyoruz.

add or remove scopes kısmına bastıktan sonra, açılan ekranda arama kısmına google drive api yazıyor, daha sonrasında ‘auth/drive’ seçeneğini seçiyor ve update diyerek ekranı kapatıyoruz. scope kısmını da save and continue diyerek geçiyoruz.

OAuth Client ID kısmında, uygulama tipimizi desktop app olarak seçiyoruz. isim kısmı opsiyonel, siz istediğiniz şekilde düzenleyebilirsiniz. create’e basıyor ve devam ediyoruz.

vee tamamladık! artık bir uygulamız var. download butonuna basarak client_secret_xx isimli json dosyamızı indiriyoruz.

indirdiğimiz dosyayı, çalışma dizinimize atıyoruz. son olarak, client_secret isimli dosyamızın adını “client_secrets.json” olarak düzenliyoruz.

google drive api için authorization dosyası elde etmek

çalışma için bir utils.py dosyası oluşturuyorum. bütün arka planda çalışacak kodlar bu dosyanın içinde olacak. amacım, main.py dosyasının sadece bir sayfa sunması ve gerekli fonksiyonları çağırarak işlemleri yönetmesi.

utils.py dosyası içinde öncelikle, daha sonrasında kullanmayacağımız fonksiyonu oluşturalım. buradaki çalışmamızın temel mantığını şu şekilde özetleyebiliriz.

peki ya kodda nasıl görünecek bu akış?

kodumuzu main.py dosyamızda çağıralım;

from utils import *

create_drive_auth()

kodumuzu çalıştırdığımız an bize bir web sayfası açılacak, çünkü elimizde bir ‘mycreds.txt’ dosyası yok.

daha sonrasında, uygulamamız çok basit tasarlandığı için, diğer kullanıcılarının güvenliği açısından şöyle bir uyarı veriyor, burada devam et diyoruz. sonuçta geliştiriciyi tanıyoruz..

devam et dedikten sonra karşımıza uygulamamızın istediği şeyler çıkıyor;

buna da devam et dedikten sonra tarayıcımız şöyle bir mesaj veriyor;

bu, doğrulama işlemini tamamladığımız anlamına gerekiyor, eğer şöyle bir uyarı görürseniz;

https://console.cloud.google.com/ adresine geri dönüyor, OAuth consent screen sekmesinden, test kullanıcısı olarak kendimizi ekliyoruz.

daha sonrasında kodumuzu tekrar çalıştırdığımızda yukarıdaki aşamalardan geçiyoruz. ‘The authentication flow has completed.’ yazısını gördüğümüzde, sekmeyi kapatarak PyCharm’a bakıyoruz.

‘mycreds.txt’ dosyasının oluştuğunu görüyoruz, artık tekrar çalıştırdığımızda herhangi bir sekme açılmayacak, program çalışıp duracak.

evet, mycreds.txt dosyasıyla daha sonra ilgileneceğiz, yolumuz uzun! şimdi streamlit alt yapısını tasarlamaya başlayalım.

streamlit ile veri okuma, işleme ve yazma süreçlerimize başlayalım!

streamlit sayfasını ve iskeleti tasarlamak

normalde diğer programcılarda gördüğüm, bütün tasarım ve dizaynları utils içinde yapıp, main içinde çağırmak. ancak ben, bütün streamlit elementlerini main.py üzerinde oluşturup, yapılacak işlemler için utils.py üzerinden fonksiyonlar çağırmayı tercih ediyorum. iskeletimizi oluşturalım;

import streamlit as st

st.title("CSV İşleme Uygulaması")
st.file_uploader("CSV Dosyasını Seçiniz", type="csv")
st.button("Dosyayı Yükle ve Analiz Et")

sadece 4 satır kodla şöyle bir sayfa elde ediyoruz;

iskeletimiz hazır, geri kalan işlemler;

kullanıcı için
- analizin yapılması,
-ekrana sonucun basılması,

bizim için
- yapılan işlemler için log kaydının tutulması,
- sonucun bir text dosyasına kaydedilmesi,
- log, sonuç ve csv dosyalarının belirlenmiş drive klasörüne yüklenmesi,

kullanıcı için yapacaklarımızdan başlayalım.

kullanıcı için analiz işlemeleri

yapacağımız her işlemde bir de arka planda log kaydı tutmak istiyoruz. tüm tanımlayacağımız fonksiyonların içinde bir log kısmı da olacak. direkt utils dosyamızın içine bir log file oluşturma komutu ekliyoruz. böylece utils.py çağrıldığında, arka planda bir log dosyası oluşmuş olacak. kullanıcılardan herhangi bir isim veya bilgi almayacağımız için ve projemizde asıl amacımız yapılanları anonim bir şekilde takip etmek olduğundan kullanıcı dosyalarını tarihle isimlendireceğiz. streamlit direkt olarak logging kütüphanesini kullanmamıza izin vermediği için (tarih itibariyle doğrudan desteği bulunmamakta) bir adet text dosyasını log dosyası olarak kullanacağız ve yazma işlemlerini fonksiyonlarımız ile yöneteceğiz.

kodumuzu biraz açmak istiyorum, çünkü normalde var olan bir fonksiyonu kendimiz için, şartlarımız için uyarladık. siz de bu yazıyı takip ederken, kendi probleminiz için uyarlamanız gerekebilir. ne yaptığını anlamak, hangi ihtiyacınız için neyi değiştireceğinizi bilmeniz için çok önemli. ilk kısımda, current_user olarak o anın tarihi alıyoruz. çünkü kullanıcılarımızı anlık tarih olarak tutmak istiyoruz, herhangi bir karışıklık olmaması için milisaniyeye kadar tarihi alıyoruz, böylece (olur mu emin değilim) aynı saniyede sisteme giriş yapan iki kullanıcı için milisaniye bazından farklılaşma yaşanacak.

ilk fonksiyonumuz, kullanıcı için bir log text dosyası oluşturuyor. bu dosyayı utf-8 formatında açıyor, böylece türkçe karakter kullandığımızda bir sorun yaşamayacağız. log mesajını ayrı bir string ile oluşturup daha sonrasında yazdırıyoruz. aslında direkt olarak log mesajının içine yazabiliriz. yaz sırasında herhangi bir problem yaşamamak adına, ayrı bir string oluşturup daha sonrasında onu kaydediyoruz. sonundaki \n, mesajdan sonra alt satıra geçilmesi. böylece loglar yan yana yazılmak yerine, alt alta tutulacak.

diğer fonksiyonumuz ise, bizden ‘INFO’ ve ‘ERROR’ şeklinde bir ön ek ve mesaj alarak logumuzu tutuyor. normalde bu işlemi python’ın logging kütüphanesi bu işlemi çok rahat bir şekilde bizim adımıza yapabiliyor. streamlit sebebiyle bunu kendimiz yapıyoruz. fonksiyomuz çalışma içerisinde olayın gerçekleşme saatini de tutuyor. böylece, olay sırasını ve aralarındaki zaman farkını rahatça takip edebiliyoruz.

import streamlit as st
from utils import *


st.title("CSV İşleme Uygulaması")
st.file_uploader("CSV Dosyasını Seçiniz", type="csv")
if st.button("Dosyayı Yükle ve Analiz Et"):
create_user_log_file()
save_to_log('INFO', 'Dosya yükleme işlemi başlatıldı.')

şu an log dosyamızın çalışma biçimini test etmek için bu şekilde kurguladık, daha sonrasında çalışma sırasında yaşanabilecek sorunları engellemek ve kaydetmek için başka bir kurgumuz olacak. programımızı çalıştırdıktan sonra, butona basacağız.

dosyamız şu şekilde görünüyor olacak

diğer loglar eklendiğinde peş peşe görünecek. evet şu ana kadar yaptığımız her şey, uygulamanın asıl amacının dışındaydı, şimdi kullanıcıdan bir CSV dosyası aldığımızda onu işleyecek kodumuzu yazalım. kodumuzun amacı basit, tek sütunluk bir CSV dosyasını alacak ve her satırı toplayıp, toplam sonucunu yazdıracak.

def process_csv(dataframe):
return dataframe.sum().values[0]

çok basit, kolay bir şekilde işlemimizi yaptık. dediğim gibi, bu yazının amacı bir işlem yapmak değil, yapılan işlemlerin kaydının tutulması.

burada bir diğer trick, utils dosyasımıza global olarak “created_files” isimli bir liste oluşturacağız. kullanıcıdan onay aldığımızda bu 3 dosyayı isim isim yükleyeceğiz. bu sebepten ötürü process_csv dosyamızı şu şekilde güncelliyoruz.

def process_csv(dataframe):
dataframe.to_csv(f"csv_{current_user}.csv", index=False)
created_files.append(f"csv_{current_user}.csv")
return dataframe.sum().values[0]

aynı işlemi log dosyası oluşturma fonksiyonumuza da yapalım.

def create_user_log_file():
# Kullanıcı için log dosyası oluşturma,
with
open(f"log_{current_user}.txt", "w", encoding="utf-8") as f:
created_files.append(f"log_{current_user}.txt")
# İlk mesaj
log_message = str(current_user + " için log dosyasının başlangıcı.\n\n")
f.write(log_message)
f.close()

şimdi, process_csv kodumuzu bir test edelim.

kendi oluşturduğumuz dosyamızı çalıştırdığımızda şöyle bir ekran görmemiz gerekiyor.

çalışma dizinimiz ise böyle görünüyor.

bu işlemi, process_csv içerisinde de yapabilirdik. ancak dışarda yapalım ve sonucumuzu bir text dosyasının içine kaydedelim. bu senaryo için aşırı gereksiz olduğunun farkındayım, ancak amacımız..

def save_results(result):
with open(f"result_{current_user}.txt", "a", encoding="utf-8") as f:
created_files.append(f"result_{current_user}.txt")
f.write(str(result))
f.close()

asıl kıyametin kopmasına çok az kaldı, son bir kez daha programımızı çalıştıralım. bu sefer, log kayıtlarımızı da düzenleyelim ve buraya bir daha yazımızın sonunda dönelim.

her adımın kaydını ve oluşabilecek hataları kullanıcılara da göstererek kaydediyoruz. şu haliyle çalıştığında dosyalarımız şöyle gözükecek.

result dosyamız
csv dosyamız, kullanıcıdan alınanla birebir aynı
son olarak, log dosyamız

evet her şey istediğimiz gibi çalışıyor. gelelim, yazımızın asıl amacına gelelim. oluşturduğumuz tüm dosyaları, bir google drive klasörüne yükleyeceğiz. buradaki asıl zorluktan bahsetmiştik ve bir ‘mycreds.txt’ dosyasını oluşturmuştuk. şimdi, mycreds.txt dosyamızı açıyoruz. elimizde JSON formatında dizi var. https://www.convertsimple.com/convert-json-to-toml/ sitesine giderek, json dosyamızı toml formatına çeviriyoruz. dosyanın içeriğini direkt olarak yapıştırmamız yeterli.

şimdi, çalıştığımız kök dizine ‘.streamlit’ isimli bir klasör oluşturuyoruz. bu klasörün içine ‘secrets.toml’ dosyası oluşturuyoruz.

JSON to TOML sitesinden oluşturduğumuz kısmı direkt olarak bu dosyanın içine yapıştırıyoruz.

repomuzu githuba yüklerken .streamlit/secrets.toml dosyasını .gitignore’a eklemeyi unutmayın. buradaki tüm veriler, streamlit tarafından okunabiliyor olacak. bunun aynı kurguyu deploy ettiğimizde de yapabileceğiz. böylece bize ait bu bilgileri public olarak paylaşmamış olacağız. programımızın, google drive ile doğrulama yapabilmesi için bir doğrulama dosyamıza ihtiyacımız var. bu dosyayı, kendimiz oluşturacağız. nasıl mı? işte böyle.

bu kodumuz sayesinde public olarak herhangi bir bilgi yansıtmadan, uygulamamıza dosya kaydetmeye çalışan kişinin biz olduğumuzu doğrulayacak dosyamızı oluşturuyoruz.

evet! bitiyor! son olarak, elde ettiğimiz dosyaları yükleyelim! öncelikle drivemıza gidip test isimli bir klasör oluşturalım.

şimdi yükleme kodumuzu yazalım.

az önce yazdığımız mycreds dosyasını oluşturacak kodumuzu hemen kodumuzun başında çağırıyoruz. o kod, bizim için arka planda ‘mycreds.txt’ dosyasını oluşturuyor. daha sonrasında, dosyadan yüklüyoruz. daha sonrasında, folder_name olarak oluşturduğumuz dosyanın adını veriyoruz. programa, dosyamızı mevcut dosyalar içinde aramasını istiyoruz. eğer, dosyamızı bulabilirse “created_file” dosyasına kaydedilmiş dosyaları drive’a yükletiyoruz. test kodumuz çalıştıktan sonra test klasörümüz şu şekilde görünüyor;

ve bitti! programımız bizden bağımsız olarak analizi yapıyor, analiz sırasında yaşanan durumları kaydediyor ve istediğimiz dosyaya yüklüyor!

yazı bir tık uzun oldu biliyorum, ancak bütün süreçleri kendim keşfettiğim için inanıyorum ve biliyorum ki kat kat verimli başka yollar da vardır. ancak, benim gibi başlangıç seviyesinde bir programcıysanız ve bu çalışma ihtiyaçlarınızı görüyorsa ne mutlu!

buraya kadar okuduğunuz için teşekkür ederim! çalışmanın tüm kaynak kodlarına buradan ulaşabilirsiniz. başka bir yazıda görüşmek üzere 🥳

edit: yazıyı bitirdikten sonra, son olarak streamlit üzerinden nasıl paylaşacağımızı belirtmediğimi fark ettim. onu da halledelim! streamlit.io sitesine gidiyor, gerekli bilgileri doldurarak kayıt oluyoruz. daha sonrasında, eğer ilk kez kaydolduysak, açılan streamlit-example sayfasını da kapatıyoruz.

new app diyerek devam ediyoruz,

uygulama github’ıma bağlı olduğu için direkt olarak çalıştığım dosyaları gördü.

buradan son olarak Advanced Settings seçeneğini seçiyoruz. buraya secrets.toml dosyamızın içeriğini ekliyoruz.

bizim uygulama içerisinde kullandığımız st.secrets fonksiyonu bütün bilgileri buradan çekecek. daha sonrasında save diyerek kapatıyor ve deploy ediyoruz. yükleme sırasında oluşabilecek hatalardan en yaygını, “xx module not found” hatasıdır. bunu önlemek için, çalışama kök dizinimize bir adet “requirements.txt” dosyası oluşturuyor ve kullandığımız bütün kütüphaneleri satır satır yazıyoruz. şimdilik bu kadar!

--

--