Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feedback #1

Open
wants to merge 12 commits into
base: feedback
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added Approach.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
13 changes: 13 additions & 0 deletions Credentials.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"type": "",
"project_id": "",
"private_key_id": "",
"private_key": "",
"client_email": "",
"client_id": "",
"auth_uri": "",
"token_uri": "",
"auth_provider_x509_cert_url": "",
"client_x509_cert_url": "",
"universe_domain": ""
}
43 changes: 37 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
[![Review Assignment Due Date](https://classroom.github.com/assets/deadline-readme-button-22041afd0340ce965d47ae6ef1cefeee28c7c493a6346c4f15d667ab976d596c.svg)](https://classroom.github.com/a/AHFn7Vbn)
# Superjoin Hiring Assignment

### Welcome to Superjoin's hiring assignment! 🚀
Expand Down Expand Up @@ -44,11 +45,11 @@ Once you're done, make sure you **record a video** showing your project working.

We have a checklist at the bottom of this README file, which you should update as your progress with your assignment. It will help us evaluate your project.

- [ ] My code's working just fine! 🥳
- [ ] I have recorded a video showing it working and embedded it in the README ▶️
- [ ] I have tested all the normal working cases 😎
- [ ] I have even solved some edge cases (brownie points) 💪
- [ ] I added my very planned-out approach to the problem at the end of this README 📜
- [*] My code's working just fine! 🥳
- [*] I have recorded a video showing it working and embedded it in the README ▶️
- [*] I have tested all the normal working cases 😎
- [*] I have even solved some edge cases (brownie points) 💪
- [*] I added my very planned-out approach to the problem at the end of this README 📜

## Got Questions❓
Feel free to check the discussions tab, you might get some help there. Check out that tab before reaching out to us. Also, did you know, the internet is a great place to explore? 😛
Expand All @@ -58,4 +59,34 @@ We're available at [email protected] for all queries.
All the best ✨.

## Developer's Section
*Add your video here, and your approach to the problem (optional). Leave some comments for us here if you want, we will be reading this :)*
Hi 👋!
The implementation is specific to google sheets and MySQL using python.
The problem statement presented a very benefical usecase, and I found out a lot of products are available in the market to solve this on a larger scale.
Google even has node support for this application!

In this repository, is my take on the same.

### To Run 🏃:
_prerequisites: python 3.8 or above installed, MySQL server installed_

- run `python -m pip install -r requirements.txt`
- Configure the [variables.env](./variables.env) with your MySQL server credentials and sheet_id.
- Configure the [Credentials.json](./Credentials.json) with your google cloud details, for more specifications see [here](https://console.cloud.google.com/apis/library/sheets.googleapis.com).
- Run basetable.sql in your MySQL database.
- You are all set!
- execute `python main.py` and the you will have your connection!

**Link to video** ➡️: [🔗] (https://drive.google.com/file/d/13qoSB5MhUInSsnRPfnhRSUhYtZu7Ufg3/view?usp=sharing)

### Approach
![approach](./Approach.png)


**Features planned but not completed due to time constraints:** ⏫
- API listeners (Flast/FastAPI) for changes.
- Object relational mapping using SQL alchemy

The project was super fun to build! Thank you for this opportunity 😊



11 changes: 11 additions & 0 deletions basetable.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
use superjoin;
show tables;
CREATE TABLE cars (
id INT PRIMARY KEY,
Name VARCHAR(50),
Color VARCHAR(50),
Model VARCHAR(50),
last_modified TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);

select * from cars;
24 changes: 24 additions & 0 deletions cursors.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import mysql.connector
import gspread
from google.oauth2.service_account import Credentials


'''MySQL Connection'''
def connect_to_mysql(host, user, password, database):
connection = mysql.connector.connect(
host=host,
user=user,
password=password,
database=database
)
cursor = connection.cursor()
return cursor, connection


'''Google Sheets Authentication'''
def authenticate_google_sheets(credentials_file, sheet_id):
scope = ['https://www.googleapis.com/auth/spreadsheets','https://www.googleapis.com/auth/drive']
credentials = Credentials.from_service_account_file(credentials_file, scopes=scope)
client = gspread.authorize(credentials)
sheet= client.open_by_key(sheet_id)
return sheet
38 changes: 38 additions & 0 deletions main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import cursors
import os
import dotenv
import sync
import time
dotenv.load_dotenv(dotenv_path='variables.env')


def bi_directional_sync(sheet, cursor, conn, interval=10):
while True:
print("Syncing from Google Sheets to MySQL...")
sync.sync_google_to_mysql(sheet, cursor, conn)

print("Syncing from MySQL to Google Sheets...")
sync.sync_mysql_to_google(sheet, cursor)

print(f"Sync completed. Waiting for {interval} seconds before next sync.")
time.sleep(interval) # Wait for the next sync cycle

# 7. Main Execution

if __name__ == "__main__":
# Initialize Google Sheets and MySQL connections
print("Initializing Google Sheets API...")
cursor,connection=cursors.connect_to_mysql(os.getenv('HOST'), os.getenv('USER'), os.getenv('PASSWORD'), os.getenv('DATABASE'))
sheet=cursors.authenticate_google_sheets('Credentials.json', os.getenv('SHEET_ID'))

try:
# Start bi-directional sync
bi_directional_sync(sheet, cursor, connection, interval=10)
except KeyboardInterrupt:
print("Sync process interrupted by user.")
finally:
cursor.close()
connection.close()
print("MySQL connection closed.")


9 changes: 9 additions & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
mysql-connector-python==8.0.33
SQLAlchemy==2.0.14
Flask-SQLAlchemy
google-api-python-client
google-auth-httplib2
google-auth-oauthlib
gspread== 6.1.2
Flask
python-dotenv
38 changes: 38 additions & 0 deletions sync.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
from datetime import datetime
def sync_google_to_mysql(sheet, cursor, conn):
# Fetch all rows from Google Sheets
records = sheet.get_worksheet(0).get_all_records()

for record in records:
last_modified_google = datetime.strptime(record['last_modified'], '%m/%d/%Y %H:%M:%S')

query = """
INSERT INTO cars (id, Name ,color, model, last_modified)
VALUES (%s, %s , %s, %s, %s)
ON DUPLICATE KEY UPDATE
Name=VALUES(Name),
color = VALUES(color),
model = VALUES(model),
last_modified = VALUES(last_modified)
"""
data = (record['id'], record['Name'], record['Color'], record['Model'], last_modified_google)
cursor.execute(query, data)
conn.commit()

def sync_mysql_to_google(sheet, cursor):
cursor.execute("SELECT id, Name, Color, Model, last_modified FROM cars")
records = cursor.fetchall()

# Convert MySQL records to Google Sheets format, including the timestamp
for i, row in enumerate(records, start=2): # Assuming first row is headers
# Update Google Sheet row
sheet.get_worksheet(0).update(f'A{i}', [[row[0], row[1], row[2], row[3], row[4].strftime('%m/%d/%Y %H:%M:%S')]])

def resolve_conflict(record_from_google, record_from_mysql):
last_modified_google = datetime.strptime(record_from_google['last_modified'], '%m/%d/%Y %H:%M:%S')
last_modified_mysql = record_from_mysql['last_modified']

if last_modified_google > last_modified_mysql:
return 'google'
else:
return 'mysql'
5 changes: 5 additions & 0 deletions variables.env
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
SHEET_ID=''
HOST=''
DATABASE=''
USER=''
PASSWORD=''