Skip to content
This repository has been archived by the owner on Feb 28, 2024. It is now read-only.

Refactor to more a more clean architecture style #4

Merged
merged 6 commits into from
Oct 24, 2023
Merged
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
4 changes: 2 additions & 2 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,6 @@ COPY requirements.txt requirements.txt

RUN pip3 install -r requirements.txt

COPY src/main.py main.py
COPY . .

CMD [ "python3", "./main.py" ]
CMD [ "python3", "main.py" ]
Empty file added __init__.py
Empty file.
38 changes: 38 additions & 0 deletions main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
from src.repositories.apartment_firestore import ApartmentFirestoreRepository
from src.usecase.apartment import ApartmentUseCase
from src.infrastructure.discord import DiscordInfrastructure
from src.repositories.scraper import ScraperRepository
from firebase_admin import firestore, initialize_app, credentials

import os
import json

def main():
scraper_repository = ScraperRepository()
apartment_repository = ApartmentFirestoreRepository(db)
discord_infra = DiscordInfrastructure(INPUT_DISCORD_WEBHOOK_URL)
apartment_usecase = ApartmentUseCase(scraper_repository, apartment_repository, discord_infra)

apartments = apartment_usecase.get_all_apartments(INPUT_APARTMENTS_URL, INPUT_APARTMENTS_FILTER)
for apartment in apartments:
if apartment_usecase.store_apartment(apartment) == True:
apartment_usecase.post_to_discord(apartment)

if __name__ == "__main__":
INPUT_DISCORD_WEBHOOK_URL = os.environ["DISCORD_WEBHOOK_URL"]
INPUT_APARTMENTS_URL = (
os.environ.get("APARTMENTS_URL")
or "https://bostad.stockholm.se/bostad/?sort=annonserad-fran-desc&ungdom=1"
)
INPUT_APARTMENTS_FILTER = os.environ.get("APARTMENTS_FILTER") # Defaults to None if env not set
if INPUT_APARTMENTS_FILTER:
try:
INPUT_APARTMENTS_FILTER = json.loads(INPUT_APARTMENTS_FILTER)
except (TypeError, json.JSONDecodeError):
raise ValueError("APARTMENTS_FILTER must be a valid JSON object")

FIRESTORE_CRED = credentials.Certificate(json.loads(os.environ["FIREBASE_ACCOUNT"]))
initialize_app(FIRESTORE_CRED)
db = firestore.client()

main()
Empty file added src/entities/__init__.py
Empty file.
41 changes: 41 additions & 0 deletions src/entities/apartment.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
class Apartment:
def __init__(self, id, address, location, last_register_date, level, rent, rooms, square_meter, temporary=False, age=None, youth=False):
self.id = id
self.address = address
self.location = location
self.last_register_date = last_register_date
self.level = level
self.link = "https://bostad.stockholm.se/bostad/" + str(id)
self.rent = rent
self.rooms = rooms
self.square_meter = square_meter
self.temporary = temporary
self.age = age
self.youth = youth

if (youth == False and age) or (age == None and youth):
raise ValueError("Both youth and age need to be set if its a youth apartment")

def get_string_id(self):
return str(self.id)

def __str__(self):
apartment = f"Address: {self.address}\n" \
f"Last Register Date: {self.last_register_date}\n" \
f"Level: {self.level}\n" \
f"Link: {self.link}\n" \
f"Location: {self.location}\n" \
f"Rent: {self.rent}\n" \
f"Rooms: {self.rooms}\n" \
f"Square Meter: {self.square_meter}\n"

if self.temporary:
apartment += f"Temporary: {self.temporary}\n"

if self.age is not None:
apartment += f"Age: {self.age}\n"

if self.youth:
apartment += f"Youth: {self.youth}\n"

return apartment
10 changes: 10 additions & 0 deletions src/infrastructure/discord.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import requests
import os

class DiscordInfrastructure:
def __init__(self, webhook_url):
self.webhook_url = webhook_url

def send_message(self, message):
full_message = {"content": message}
requests.post(self.webhook_url, json=full_message, timeout=5)
208 changes: 0 additions & 208 deletions src/main.py

This file was deleted.

Empty file added src/repositories/__init__.py
Empty file.
19 changes: 19 additions & 0 deletions src/repositories/apartment_firestore.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import os

from google.api_core import exceptions

from src.entities.apartment import Apartment

class ApartmentFirestoreRepository:
def __init__(self, db):
self.db = db
self.collection_name = "apartment_listings"

def post_apartment_listing(self, apartment: Apartment):
document_id = apartment.get_string_id()
doc = self.db.collection(self.collection_name).document(document_id)
try:
doc.create({}) # Empty document as we only use the document_id
except exceptions.AlreadyExists:
return False
return True
15 changes: 15 additions & 0 deletions src/repositories/scraper.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
from bs4 import BeautifulSoup
from selenium import webdriver
import time

class ScraperRepository:
def scrape(self, url, sleep=1):
options = webdriver.ChromeOptions()
options.add_argument("headless")
options.add_argument("--no-sandbox")

with webdriver.Chrome(options=options) as driver:
driver.get(url) # Fetch url
time.sleep(sleep) # Wait for JS to load
soup = BeautifulSoup(driver.page_source, "lxml")
return soup
Loading
Loading