Skip to content

Commit 844ef43

Browse files
committed
Update repo to semi-working state.
1 parent 0087114 commit 844ef43

File tree

14 files changed

+1961
-440
lines changed

14 files changed

+1961
-440
lines changed

.flake8

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
[flake8]
2+
select = E9,F63,F7,F82
3+
exclude = .git,.github,__pycache__,.pytest_cache,.venv,logs,creds
4+
max-line-length = 120

.gitignore

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -106,10 +106,11 @@ venv.bak/
106106
# DS Store
107107
.DS_Store
108108

109+
# Creds
109110
HackersAndSlackers-d2a47db89384.json
110111
HackersAndSlackers-d2a47db89384.json
111112
HackersAndSlackers-4893023543f3.json
112113
HackersAndSlackers-59ed81beb2ea.json
113114
HackersAndSlackers-aec129ee8154.json
114-
.env
115-
.DS_Store
115+
gcloud.json
116+

Makefile

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
PROJECT_NAME := $(shell basename $CURDIR)
2+
VIRTUAL_ENV := $(CURDIR)/.venv
3+
LOCAL_PYTHON := $(VIRTUAL_ENV)/bin/python3
4+
5+
define HELP
6+
Manage $(PROJECT_NAME). Usage:
7+
8+
make run - Run $(PROJECT_NAME).
9+
make install - Create virtual env, install dependencies, and run project.
10+
make deploy - Install and run script by running `make install` and `make run` in succession.
11+
make update - Update pip dependencies via Poetry and export output to requirements.txt.
12+
make format - Format code with Pythons `Black` library.
13+
make lint - Check code formatting with `flake8`.
14+
make clean - Remove cached files and lock files.
15+
16+
endef
17+
export HELP
18+
19+
20+
.PHONY: run install deploy update format lint clean help
21+
22+
all help:
23+
@echo "$$HELP"
24+
25+
26+
env: $(VIRTUAL_ENV)
27+
28+
29+
$(VIRTUAL_ENV):
30+
if [ ! -d $(VIRTUAL_ENV) ]; then \
31+
echo "Creating Python virtual env in \`${VIRTUAL_ENV}\`"; \
32+
python3 -m venv $(VIRTUAL_ENV); \
33+
fi
34+
35+
36+
.PHONY: run
37+
run: env
38+
$(LOCAL_PYTHON) -m main:init_script
39+
40+
41+
.PHONY: install
42+
install: env
43+
$(LOCAL_PYTHON) -m pip install --upgrade pip setuptools wheel && \
44+
$(LOCAL_PYTHON) -m pip install -r requirements.txt && \
45+
echo Installed dependencies in \`${VIRTUAL_ENV}\`;
46+
47+
48+
.PHONY: deploy
49+
deploy:
50+
make install && \
51+
make run
52+
53+
54+
.PHONY: update
55+
update: env
56+
$(LOCAL_PYTHON) -m pip install --upgrade pip setuptools wheel && \
57+
poetry update && \
58+
poetry export -f requirements.txt --output requirements.txt --without-hashes && \
59+
echo Installed dependencies in \`${VIRTUAL_ENV}\`;
60+
61+
62+
.PHONY: format
63+
format: env
64+
isort --multi-line=3 . && \
65+
black .
66+
67+
68+
.PHONY: lint
69+
lint:
70+
flake8 . --count \
71+
--select=E9,F63,F7,F82 \
72+
--exclude .git,.github,__pycache__,.pytest_cache,.venv,logs,creds,.venv,docs,logs \
73+
--show-source \
74+
--statistics
75+
76+
77+
.PHONY: clean
78+
clean:
79+
find . -name '.coverage' -delete && \
80+
find . -name '*.pyc' -delete \
81+
find . -name '__pycache__' -delete \
82+
find . -name 'poetry.lock' -delete \
83+
find . -name '*.log' -delete \
84+
find . -name '.DS_Store' -delete \
85+
find . -wholename '**/*.pyc' -delete && \
86+
find . -wholename '**/*.html' -delete && \
87+
find . -type d -wholename '__pycache__' -exec rm -rf {} + && \
88+
find . -type d -wholename '.venv' -exec rm -rf {} + && \
89+
find . -type d -wholename '.pytest_cache' -exec rm -rf {} + && \
90+
find . -type d -wholename '**/.pytest_cache' -exec rm -rf {} + && \
91+
find . -type d -wholename '**/*.log' -exec rm -rf {} + && \
92+
find . -type d -wholename './.reports/*' -exec rm -rf {} + && \
93+
find . -type d -wholename '**/.webassets-cache' -exec rm -rf {};

Pipfile

Lines changed: 0 additions & 12 deletions
This file was deleted.

Pipfile.lock

Lines changed: 0 additions & 285 deletions
This file was deleted.

README.md

Lines changed: 8 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,32 @@
11
# Google Cloud Storage Python SDK Tutorial
22

3-
![Python](https://img.shields.io/badge/Python-v3.7-blue.svg?logo=python&longCache=true&logoColor=white&colorB=5e81ac&style=flat-square&colorA=4c566a)
4-
![Google Cloud Storage](https://img.shields.io/badge/Google--Cloud--Storage-v1.16.1-blue.svg?logo=Google&longCache=true&logoColor=white&colorB=5e81ac&style=flat-square&colorA=4c566a)
3+
![Python](https://img.shields.io/badge/Python-v3.10-blue.svg?logo=python&longCache=true&logoColor=white&colorB=5e81ac&style=flat-square&colorA=4c566a)
4+
![Google Cloud Storage](https://img.shields.io/badge/Google--Cloud--Storage-v2.18.0-blue.svg?logo=Google&longCache=true&logoColor=white&colorB=5e81ac&style=flat-square&colorA=4c566a)
55
![GitHub Last Commit](https://img.shields.io/github/last-commit/google/skia.svg?style=flat-square&colorA=4c566a&colorB=a3be8c)
66
[![GitHub Issues](https://img.shields.io/github/issues/toddbirchard/tableau-extraction.svg?style=flat-square&colorA=4c566a&colorB=ebcb8b)](https://github.com/hackersandslackers/googlecloud-storage-tutorial/issues)
77
[![GitHub Stars](https://img.shields.io/github/stars/toddbirchard/tableau-extraction.svg?style=flat-square&colorB=ebcb8b&colorA=4c566a)](https://github.com/hackersandslackers/googlecloud-storage-tutorial/stargazers)
88
[![GitHub Forks](https://img.shields.io/github/forks/toddbirchard/tableau-extraction.svg?style=flat-square&colorA=4c566a&colorB=ebcb8b)](https://github.com/hackersandslackers/googlecloud-storage-tutorial/network)
99

1010
![Google Cloud Storage Python SDK Tutorial](https://storage.googleapis.com/hackersandslackers-cdn/2019/06/[email protected])
1111

12-
Source for the accompanying tutorial: https://hackersandslackers.com/manage-files-in-google-cloud-storage-with-python/
12+
Source for the accompanying tutorial here: [https://hackersandslackers.com/google-cloud-storage-with-python/](https://hackersandslackers.com/google-cloud-storage-with-python/)
1313

1414
## Getting Started
1515

16-
Installation is recommended with Pipenv:
16+
Installation is recommended via Makefile
1717

1818
```shell
1919
$ git clone https://github.com/hackersandslackers/googlecloud-storage-tutorial.git
2020
$ cd googlecloud-storage-tutorial
21-
$ pipenv shell
22-
$ pipenv update
23-
$ python3 main.py
21+
$ make install
2422
```
2523

26-
Alternatively, try installing via `setup.py`:
24+
### Usage
2725

2826
```shell
29-
$ git clone https://github.com/hackersandslackers/googlecloud-storage-tutorial.git
30-
$ cd googlecloud-storage-tutorial
31-
$ python3 setup.py install
32-
$ python3 main.py
27+
$ make run
3328
```
29+
3430
-----
3531

3632
**Hackers and Slackers** tutorials are free of charge. If you found this tutorial helpful, a [small donation](https://www.buymeacoffee.com/hackersslackers) would be greatly appreciated to keep us in business. All proceeds go towards coffee, and all coffee goes towards more content.

config.py

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
11
"""Google Cloud Storage Configuration."""
22
from os import environ
33

4-
54
# Google Cloud Storage
6-
bucketURL = environ.get('GCP_BUCKET_URL')
7-
bucketName = environ.get('GCP_BUCKET_NAME')
8-
bucketFolder = environ.get('GCP_BUCKET_FOLDER_NAME')
5+
bucket_url = environ.get("GCP_BUCKET_URL")
6+
bucket_name = environ.get("GCP_BUCKET_NAME")
7+
bucket_dir = environ.get("GCP_BUCKET_FOLDER_NAME")
98

109
# Data
11-
localFolder = environ.get('LOCAL_FOLDER')
10+
local_dir = environ.get("LOCAL_FOLDER")
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
"""Initialize script demonstration"""
2+
from config import bucket_dir, bucket_name, local_dir
3+
from googlecloud_storage_tutorial.storage import (
4+
delete_file,
5+
download_random_file,
6+
list_files,
7+
rename_file,
8+
upload_files,
9+
)
10+
11+
12+
def init_script():
13+
print(upload_files(bucket_name))
14+
print(list_files(bucket_name))
15+
print(download_random_file(bucket_name, bucket_dir, local_dir))
16+
print(rename_file(bucket_name, bucket_dir, "test.csv", "sample_test.csv"))
17+
print(delete_file(bucket_name, bucket_dir, "sample_text.txt"))
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
"""Programmatically interact with a Google Cloud Storage bucket."""
2+
from os import listdir
3+
from os.path import isfile, join
4+
from random import randint
5+
from typing import List, Optional
6+
7+
from google.cloud import storage
8+
9+
from config import bucket_dir, bucket_name, local_dir
10+
11+
storage_client = storage.Client()
12+
bucket = storage_client.get_bucket(bucket_name)
13+
14+
15+
def upload_files(bucket_name: str, bucket_dir: str, local_dir: str) -> str:
16+
"""
17+
Upload files to GCP bucket.
18+
19+
:param str bucket_name: Human-readable GCP bucket name.
20+
:param str bucket_dir: Bucket directory in which object exists.
21+
:param str local_dir: Local file path to upload/download files.
22+
23+
:returns: str
24+
"""
25+
files = [f for f in listdir(local_dir) if isfile(join(local_dir, f))]
26+
for file in files:
27+
local_file = local_dir + file
28+
blob = bucket.blob(bucket_dir + file)
29+
blob.upload_from_filename(local_file)
30+
return f"Uploaded {files.join(', ')} to '{bucket_name}' bucket."
31+
32+
33+
def list_files(bucket_dir: str) -> List[Optional[str]]:
34+
"""
35+
List all objects with file extension in a GCP bucket.
36+
37+
:param str bucket_name: Human-readable GCP bucket name.
38+
:param str bucket_dir: Bucket directory in which object exists.
39+
40+
:returns: List[Optional[str]]
41+
"""
42+
files = bucket.list_blobs(prefix=bucket_dir)
43+
file_list = [file.name for file in files if "." in file.name]
44+
return file_list
45+
46+
47+
def download_random_file(bucket_name: str, local_dir: str) -> str:
48+
"""
49+
Download random file from GCP bucket.
50+
51+
:param str bucket_name: Human-readable GCP bucket name.
52+
:param str bucket_dir: Bucket directory in which object exists.
53+
:param str local_dir: Local file path to upload/download files.
54+
55+
:returns: str
56+
"""
57+
fileList = list_files(bucket_name)
58+
rand = randint(0, len(fileList) - 1)
59+
blob = bucket.blob(fileList[rand])
60+
file_name = blob.name.split("/")[-1]
61+
blob.download_to_filename(local_dir + file_name)
62+
return f"{file_name} downloaded from bucket."
63+
64+
65+
def delete_file(bucket_name: str, bucket_dir: str) -> str:
66+
"""
67+
Delete file from GCP bucket.
68+
69+
:param str bucket_name: Human-readable GCP bucket name.
70+
:param str bucket_dir: Bucket directory in which object exists.
71+
72+
:returns: str
73+
"""
74+
blob = bucket.blob(bucket_dir + file_name)
75+
file_name = blob.name.split("/")[-1]
76+
bucket.delete_blob(bucket_name + file_name)
77+
return f"{file_name} deleted from bucket: {bucket_name}."
78+
79+
80+
def rename_file(bucket_dir: str, new_filename: str) -> str:
81+
"""
82+
Rename a file in GCP bucket.
83+
84+
:param str bucket_name: Human-readable GCP bucket name.
85+
:param str bucket_dir: Bucket directory from which to extract object.
86+
:param str new_filename: New file name for Blob object.
87+
88+
:returns: str
89+
"""
90+
blob = bucket.blob(bucket_dir + file_name)
91+
file_name = blob.name.split("/")[-1]
92+
bucket.rename_blob(blob, new_name=new_filename)
93+
return f"{file_name} renamed to {new_filename}."

main.py

Lines changed: 3 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -1,64 +1,4 @@
1-
"""Programatically interact with a Google Cloud Storage bucket."""
2-
from os import listdir
3-
from os.path import isfile, join
4-
from random import randint
5-
from google.cloud import storage
6-
from config import bucketName, localFolder, bucketFolder
1+
from googlecloud_storage_tutorial import init_script
72

8-
storage_client = storage.Client()
9-
bucket = storage_client.get_bucket(bucketName)
10-
11-
12-
def upload_files(bucketName):
13-
"""Upload files to GCP bucket."""
14-
files = [f for f in listdir(localFolder) if isfile(join(localFolder, f))]
15-
for file in files:
16-
localFile = localFolder + file
17-
blob = bucket.blob(bucketFolder + file)
18-
blob.upload_from_filename(localFile)
19-
return f'Uploaded {files} to "{bucketName}" bucket.'
20-
21-
22-
def list_files(bucketName):
23-
"""List all files in GCP bucket."""
24-
files = bucket.list_blobs(prefix=bucketFolder)
25-
fileList = [file.name for file in files if '.' in file.name]
26-
return fileList
27-
28-
29-
def download_random_file(bucketName, bucketFolder, localFolder):
30-
"""Download random file from GCP bucket."""
31-
fileList = list_files(bucketName)
32-
rand = randint(0, len(fileList) - 1)
33-
blob = bucket.blob(fileList[rand])
34-
fileName = blob.name.split('/')[-1]
35-
blob.download_to_filename(localFolder + fileName)
36-
return f'{fileName} downloaded from bucket.'
37-
38-
39-
def delete_file(bucketName, bucketFolder, fileName):
40-
"""Delete file from GCP bucket."""
41-
bucket.delete_blob(bucketFolder + fileName)
42-
return f'{fileName} deleted from bucket.'
43-
44-
45-
def rename_file(bucketName, bucketFolder, fileName, newFileName):
46-
"""Rename file in GCP bucket."""
47-
blob = bucket.blob(bucketFolder + fileName)
48-
bucket.rename_blob(blob,
49-
new_name=newFileName)
50-
return f'{fileName} renamed to {newFileName}.'
51-
52-
53-
print(upload_files(bucketName))
54-
print(list_files(bucketName))
55-
print(download_random_file(bucketName,
56-
bucketFolder,
57-
localFolder))
58-
print(rename_file(bucketName,
59-
bucketFolder,
60-
'test.csv',
61-
'sample_test.csv'))
62-
print(delete_file(bucketName,
63-
bucketFolder,
64-
'sample_text.txt'))
3+
if __name__ == "__main__":
4+
init_script()

0 commit comments

Comments
 (0)