commit 0203e73cd1017880224303b7d8c17489c2d1b571 Author: Amritanshu Date: Fri Jul 7 06:38:14 2023 +0530 Inital Commit diff --git a/.flake8 b/.flake8 new file mode 100644 index 0000000..9bfe580 --- /dev/null +++ b/.flake8 @@ -0,0 +1,4 @@ +[flake8] +ignore = E203, W503, E501 +max-line-length = 120 +exclude = .git,__pycache__,__init__.py,.mypy_cache,.pytest_cache diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..600fd86 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +*.pyc +*/__pycache__/ +*.egg-info/ +dist/ +poetry.lock \ No newline at end of file diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..92c62f3 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,29 @@ +# See https://pre-commit.com for more information +# See https://pre-commit.com/hooks.html for more hooks +repos: + - repo: https://github.com/psf/black + rev: 23.3.0 + hooks: + - id: black + + - repo: https://github.com/PyCQA/flake8 + rev: 6.0.0 + hooks: + - id: flake8 + args: ['--config=brewman/.flake8'] + + - repo: https://github.com/timothycrosley/isort + rev: 5.12.0 + hooks: + - id: isort + additional_dependencies: [toml] + exclude: ^.*/?setup\.py$ + + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.3.0 + hooks: + - id: trailing-whitespace + - id: end-of-file-fixer + - id: check-yaml + - id: check-added-large-files + - id: debug-statements diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..d887d9f --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,16 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": "Python: FastAPI", + "type": "python", + "request": "launch", + "module": "gru", + "args": [], + "justMyCode": true + } + ] +} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..31a6802 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,16 @@ +{ + "python.defaultInterpreterPath": "/home/tanshu/.cache/pypoetry/virtualenvs/gru-uwCeNnAP-py3.11/bin/python", + "files.exclude": { + "**/__pycache__": true, + "**/.mypy_cache": true, + ".idea": true, + "**/node_modules": true, + }, + "search.exclude": { + "**/__pycache__": true, + "**/.mypy_cache": true, + ".idea": true, + "**/node_modules": true, + "**/package-lock.json": true, + } +} \ No newline at end of file diff --git a/CHANGES.md b/CHANGES.md new file mode 100644 index 0000000..35a34f3 --- /dev/null +++ b/CHANGES.md @@ -0,0 +1,4 @@ +0.0 +--- + +- Initial version diff --git a/README.md b/README.md new file mode 100644 index 0000000..60d67b2 --- /dev/null +++ b/README.md @@ -0,0 +1,21 @@ +## Prettier - to format typescript files +```npx prettier --write src/app/``` + +## Eslint - to lint typescript files +```shell script +eslint src/app/ +``` +Optional to fix the errors as well +``` +eslint src/app/ --fix +``` + +### Poerty remove environment +```sh +rm -rf `poetry env info -p` +``` + +### Poerty install +```sh +poetry install +``` diff --git a/deploy.sh b/deploy.sh new file mode 100755 index 0000000..d07aa89 --- /dev/null +++ b/deploy.sh @@ -0,0 +1,31 @@ +#!/usr/bin/env bash +set -euo pipefail +parent_path=$( cd "$(dirname "${BASH_SOURCE[0]}")" || exit ; pwd -P ) +cd "$parent_path" || exit + +#./lint.sh + +# if [ 1 -eq "$#" ] +# then +# ./version_bump.sh "$1" +# else +# ./version_bump.sh +# fi + +# Download the package.json for caching +curl --silent 'https://git.tanshu.com/tanshu/gru/raw/tag/latest/pyproject.toml' \ + | sed 's/version = \"[0-9\.]*\"/version = "0.0.0"/g' \ + > "$parent_path/docker/app/pyproject.toml" + + +cd "$parent_path/docker/app" || exit +docker build --tag gru:latest . +if [ 1 -eq "$#" ] +then + docker tag gru:latest "$1" +else + echo "No version bump" +fi +cd "$parent_path/docker" || exit +docker save gru:latest | bzip2 | pv | ssh beacon 'bunzip2 | sudo docker load' +ansible-playbook --limit=beacon playbook.yml diff --git a/docker/.gitignore b/docker/.gitignore new file mode 100644 index 0000000..b5a2328 --- /dev/null +++ b/docker/.gitignore @@ -0,0 +1,2 @@ +app/package.json +app/pyproject.toml diff --git a/docker/app/Dockerfile b/docker/app/Dockerfile new file mode 100644 index 0000000..aa0bcb4 --- /dev/null +++ b/docker/app/Dockerfile @@ -0,0 +1,23 @@ +FROM python:3.11 +LABEL maintainer="Amritanshu " + +ADD https://git.tanshu.com/api/v1/repos/tanshu/gru/tags /tags.json +RUN git clone --single-branch --depth 1 --branch latest https://git.tanshu.com/tanshu/gru.git /app + +# Install Poetry +RUN curl -sSL https://raw.githubusercontent.com/python-poetry/poetry/master/install-poetry.py | POETRY_HOME=/opt/poetry python && \ + cd /usr/local/bin && \ + ln -s /opt/poetry/bin/poetry && \ + poetry config virtualenvs.create false + +WORKDIR /app + +# Allow installing dev dependencies to run tests +ARG INSTALL_DEV=false +RUN bash -c "if [ $INSTALL_DEV == 'true' ] ; then poetry install --no-root ; else poetry install --no-root --only main ; fi" + +ENV PYTHONPATH=/app +EXPOSE 80 +VOLUME /data + +CMD ["poetry", "run", "python", "-m", "gru"] diff --git a/docker/files/.env b/docker/files/.env new file mode 100644 index 0000000..8926394 --- /dev/null +++ b/docker/files/.env @@ -0,0 +1,3 @@ +HOST=0.0.0.0 +PORT=80 +FILE_PATH=/data/readings.csv diff --git a/docker/files/nginx.conf.j2 b/docker/files/nginx.conf.j2 new file mode 100644 index 0000000..284446e --- /dev/null +++ b/docker/files/nginx.conf.j2 @@ -0,0 +1,13 @@ +server { + + listen 80; + server_name {{ http_host }}; + + location / { + proxy_set_header Host $host:$server_port; + proxy_set_header X-Scheme $scheme; + proxy_set_header X-Forwarded-For $remote_addr; + proxy_pass http://localhost:{{ host_port }}; + } +} + diff --git a/docker/playbook.yml b/docker/playbook.yml new file mode 100755 index 0000000..6d92a0e --- /dev/null +++ b/docker/playbook.yml @@ -0,0 +1,75 @@ +################################################# +# DO Community Playbooks: Docker +################################################# +--- +- hosts: gru + become: true + vars_files: + - "vars/gru.yml" + + tasks: + - name: Copy dockerfile + synchronize: + src: app + dest: "/tmp/{{ host_directory }}" + + - name: Build gru image + docker_image: + name: brewman:latest + build: + path: "/tmp/{{ host_directory }}/" + dockerfile: "/tmp/{{ host_directory }}/Dockerfile" + pull: yes + state: present + source: build + + - name: Ensure Host Directory exists + file: + path: "/var/lib/{{ host_directory }}" + state: directory + + - name: Upload the .env file + template: + src: "{{ env_file }}" + dest: "/var/lib/{{ host_directory }}/.env" + + - name: Create gru container + docker_container: + name: "{{ host_directory }}" + image: gru:latest + state: started + restart_policy: "unless-stopped" + env_file: "/var/lib/{{ host_directory }}/.env" + published_ports: + - "127.0.0.1:{{ host_port }}:80" + volumes: + - "/var/lib/{{ host_directory }}:/data" + + + - name: Check if Nginx conf file exists + stat: path="/etc/nginx/sites-available/{{ http_conf }}" + register: status + + - name: No need to reload Nginx + debug: msg= {{ "No need to reload Nginx as sites-available entries have already been created" }} + + - name: Set Nginx conf file + when: status.stat.exists == false + template: + src: "files/nginx.conf.j2" + dest: "/etc/nginx/sites-available/{{ http_conf }}" + + - name: Enable new site + when: status.stat.exists == false + file: + src: "/etc/nginx/sites-available/{{ http_conf }}" + dest: "/etc/nginx/sites-enabled/{{ http_conf }}" + state: link + notify: Reload Nginx + + handlers: + - name: Reload Nginx + service: + name: nginx + state: reloaded + diff --git a/docker/vars/gru.yml b/docker/vars/gru.yml new file mode 100644 index 0000000..7ef0b2d --- /dev/null +++ b/docker/vars/gru.yml @@ -0,0 +1,6 @@ +--- +http_host: "sensors.tanshu.com" +http_conf: "sensors.tanshu.com.conf" +host_port: "8745" +host_directory: "sensors" +env_file: "files/.env" \ No newline at end of file diff --git a/gru/.env b/gru/.env new file mode 100644 index 0000000..3f83f5a --- /dev/null +++ b/gru/.env @@ -0,0 +1,3 @@ +HOST=0.0.0.0 +PORT=7458 +FILE_PATH=readings.csv \ No newline at end of file diff --git a/gru/__init__.py b/gru/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/gru/__main__.py b/gru/__main__.py new file mode 100644 index 0000000..59d3ccc --- /dev/null +++ b/gru/__main__.py @@ -0,0 +1,4 @@ +from gru.main import init + + +init() diff --git a/gru/config.py b/gru/config.py new file mode 100644 index 0000000..4b5fa5e --- /dev/null +++ b/gru/config.py @@ -0,0 +1,19 @@ +from typing import Any + +from dotenv import load_dotenv +from pydantic import BaseSettings + + +class Settings(BaseSettings): + # openssl rand -hex 32 + FILE_PATH: str = "/data/readings.csv" + HOST: str = "0.0.0.0" + PORT: int = 80 + + class Config: + case_sensitive = True + env_file = ".env" + + +load_dotenv() +settings = Settings() diff --git a/gru/main.py b/gru/main.py new file mode 100644 index 0000000..eda8ba0 --- /dev/null +++ b/gru/main.py @@ -0,0 +1,33 @@ +from typing import Annotated +import csv +import uvicorn +import datetime +from fastapi import FastAPI, Form + +from .core.config import settings +from pydantic import BaseModel + +app = FastAPI() + + +class Reading(BaseModel): + temperature: float + humidity: float + age: int + +@app.post("/upload", response_model=Reading) +def add_reading(temp: Annotated[float, Form()], humidity: Annotated[float, Form()], age: Annotated[int, Form()], device: Annotated[str, Form()]): + current_datetime = datetime.datetime.now() + deducted_datetime = current_datetime - datetime.timedelta(milliseconds=age) + append_to_csv(settings.FILE_PATH, temp, humidity, deducted_datetime) + return Reading(temperature=temp,humidity=humidity,age=age) + + +def append_to_csv(filename, temperature, humidity, datetime_var): + with open(filename, 'a', newline='') as csvfile: + writer = csv.writer(csvfile) + writer.writerow([temperature, humidity, datetime_var]) + + +def init(): + uvicorn.run(app, host=settings.HOST, port=settings.PORT) diff --git a/lint.sh b/lint.sh new file mode 100755 index 0000000..18c7ff0 --- /dev/null +++ b/lint.sh @@ -0,0 +1,10 @@ +#!/usr/bin/env bash +set -eEu -o pipefail +shopt -s extdebug +IFS=$'\n\t' + +parent_path=$( cd "$(dirname "${BASH_SOURCE[0]}")" || exit ; pwd -P ) +cd "$parent_path" || exit +isort gru +black gru +flake8 gru diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..5e46fc5 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,77 @@ +[tool.poetry] +name = "gru" +version = "1.0.0" +description = "Sensor manager." +authors = ["tanshu "] + +[tool.poetry.dependencies] +python = "^3.11" +uvicorn = {extras = ["standard"], version = "^0.22.0"} +fastapi = "^0.99.1" +python-jose = {extras = ["cryptography"], version = "^3.3.0"} +passlib = {extras = ["bcrypt"], version = "^1.7.4"} +python-multipart = "^0.0.6" +itsdangerous = "^2.1.2" +python-dotenv = "^1.0.0" +openpyxl = "^3.1.2" + +[tool.poetry.dev-dependencies] +flake8 = "^6.0.0" +black = "^23.3.0" +ruff = "^0.0.277" +pre-commit = "^3.3.3" +mypy = "^1.4.1" + +[build-system] +requires = ["poetry-core>=1.0.0"] +build-backend = "poetry.core.masonry.api" + + +[tool.mypy] +# --strict +disallow_any_generics = true +disallow_subclassing_any = true +disallow_untyped_calls = true +disallow_untyped_defs = true +disallow_incomplete_defs = true +check_untyped_defs = true +disallow_untyped_decorators = true +no_implicit_optional = true +warn_redundant_casts = true +warn_unused_ignores = true +warn_return_any = true +warn_unused_configs = true +strict_equality = true +plugins = ["sqlalchemy.ext.mypy.plugin"] +# --strict end + +[tool.isort] +profile = "black" +atomic = true +include_trailing_comma = true +lines_after_imports = 2 +lines_between_types = 1 +use_parentheses = true +src_paths = ["poetry", "tests"] +skip_glob = ["*/setup.py"] +filter_files = true +known_first_party = "poetry" + +[tool.black] +line-length = 120 +include = '\.pyi?$' +exclude = ''' +/( + \.eggs + | \.git + | \.hg + | \.mypy_cache + | \.tox + | \.venv + | _build + | buck-out + | build + | dist + | tests/.*/setup.py +)/ +'''