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

Package bread bucket as a mobile application #7

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from
Draft
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
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -260,3 +260,6 @@ dist

**/*.buckets
*.css
!/build
/build/*
!/build/.gitkeep
43 changes: 43 additions & 0 deletions MobileDockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# syntax=docker/dockerfile:1
FROM ubuntu:20.04

ENV TZ=America/New_York
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone

RUN dpkg --add-architecture i386
RUN apt update
RUN apt-get install -y wget build-essential ccache git zlib1g-dev python3 python3-dev python3-pip libncurses5:i386 libstdc++6:i386 zlib1g:i386 unzip ant ccache autoconf libtool libssl-dev libffi-dev zip

# Download Android SDK
RUN wget https://dl.google.com/android/repository/commandlinetools-linux-7583922_latest.zip
RUN unzip commandlinetools-linux-7583922_latest.zip
RUN mkdir /android-sdk
RUN yes | /cmdline-tools/bin/sdkmanager --sdk_root=/android-sdk --licenses
RUN /cmdline-tools/bin/sdkmanager --sdk_root=/android-sdk "platforms;android-27"
ENV ANDROIDSDK=/android-sdk

RUN /cmdline-tools/bin/sdkmanager --sdk_root=/android-sdk "build-tools;28.0.2"

# Download Android NDK
RUN wget https://dl.google.com/android/repository/android-ndk-r19c-linux-x86_64.zip
RUN unzip android-ndk-r19c-linux-x86_64.zip
ENV ANDROIDNDK=/android-ndk-r19c
ENV ANDROIDAPI="27"
ENV NDKAPI="21"

RUN mkdir /app
COPY main.py /app
COPY /app /app/app

RUN pip install python-for-android cython virtualenv

# Purge open jdk and reinstall becuase one of the deps above pulls stuff it should not
RUN apt-get remove -y openjdk*
RUN apt-get install -y openjdk-8-jdk-headless

COPY build-mobile.sh /build-mobile.sh
RUN chmod +x build-mobile.sh

RUN mkdir /build

CMD sh /build-mobile.sh
28 changes: 17 additions & 11 deletions app/__init__.py
Original file line number Diff line number Diff line change
@@ -1,37 +1,43 @@
import os

from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from sqlalchemy.pool import NullPool
from flask_login import LoginManager
from . import constants

db = SQLAlchemy(engine_options={
'echo': False,
'echo_pool': False,
'poolclass': NullPool
})

login_manager = LoginManager()


app = Flask(__name__)

app.config.from_mapping(
SECRET_KEY='dev'
)

app.config['HTTP_BASIC_AUTH_USERNAME'] = os.environ.get("HTTP_BASIC_AUTH_USERNAME", default='buckets')
app.config['HTTP_BASIC_AUTH_PASSWORD'] = os.environ.get("HTTP_BASIC_AUTH_PASSWORD", default='dev')
if constants.RUNNING_ON_ANDROID:
from android.permissions import request_permissions, Permission
from android.storage import primary_external_storage_path

request_permissions([Permission.WRITE_EXTERNAL_STORAGE])

BASE_DIR = os.path.join(primary_external_storage_path() , 'Documents')
db_file = os.path.join(BASE_DIR, "db.buckets")
else:
from flask_login import LoginManager
app.config['HTTP_BASIC_AUTH_USERNAME'] = os.environ.get("HTTP_BASIC_AUTH_USERNAME", default='buckets')
app.config['HTTP_BASIC_AUTH_PASSWORD'] = os.environ.get("HTTP_BASIC_AUTH_PASSWORD", default='dev')
login_manager = LoginManager()
login_manager.init_app(app)
login_manager.login_view = '/login'
db_file = os.environ.get("DB_FILE", default='/app/db.buckets')

db_file = os.environ.get("DB_FILE", default='/app/db.buckets')
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///' + db_file
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False

app.logger.warn("Connecting to SQLite Database File: %s", db_file)
db.init_app(app)
login_manager.init_app(app)

from . import views
app.register_blueprint(views.views)
login_manager.login_view = '/login'

3 changes: 3 additions & 0 deletions app/constants.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
from os import environ

RUNNING_ON_ANDROID = "ANDROID_ARGUMENT" in environ
13 changes: 13 additions & 0 deletions app/mocks.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
class LimiterMock(object):
def limit(self, _):
def inner(func):
return func
return inner

class LoginManagerMock(object):
def user_loader(self, func):
return func
def login_required(self, func):
return func
def login_user(self, func):
return func
8 changes: 3 additions & 5 deletions app/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ def active_accounts():
.query
.outerjoin(Transaction)
.group_by(Account.id)
.where(Account.closed==0)
.filter(Account.closed==0)
.order_by(func.count(Transaction.id).desc(), Account.name))

def __repr__(self):
Expand All @@ -41,15 +41,13 @@ class Transaction(db.Model):

@staticmethod
def top(limit, account=None):
tx = (Transaction
return (Transaction
.query
.where(
.filter(
or_(((account is not None) and (Transaction.account_id==account))
,(account is None)))
.order_by(Transaction.posted.desc())
.limit(limit))
print(tx)
return tx

def __repr__(self):
return '<Transaction %r>' % self.id
Expand Down
24 changes: 18 additions & 6 deletions app/views.py
Original file line number Diff line number Diff line change
@@ -1,25 +1,36 @@
from flask import request, render_template, redirect, Blueprint, current_app
from flask_login import login_user, login_required
from flask_limiter import Limiter
from flask_limiter.util import get_remote_address
from sqlalchemy import exc
from werkzeug.security import check_password_hash, generate_password_hash

from app import db, login_manager
from app import db
from app.constants import RUNNING_ON_ANDROID
from app.forms import TransactionEntryForm, LoginForm
from app.mocks import LimiterMock, LoginManagerMock
from app.models import Transaction, Account, User

DEFAULT_LIMIT = 10

views = Blueprint('views', __name__)
limiter = Limiter(current_app, key_func=get_remote_address)

if RUNNING_ON_ANDROID:
limiter = LimiterMock()
login_manager = LoginManagerMock()
login_required = login_manager.login_required
login_user = login_manager.login_user
else:
from flask_limiter.util import get_remote_address
from flask_limiter import Limiter
from flask_login import login_user, login_required
from app import login_manager
limiter = Limiter(current_app, key_func=get_remote_address)

@login_manager.user_loader
def user_loader(_):
from werkzeug.security import generate_password_hash
user = User(current_app.config['HTTP_BASIC_AUTH_USERNAME'], generate_password_hash(current_app.config['HTTP_BASIC_AUTH_PASSWORD']))
return user

def verify_password(user, username, password):
from werkzeug.security import check_password_hash
if username == user.id and check_password_hash(user.password_hash, password):
return username

Expand All @@ -44,6 +55,7 @@ def root():

@views.route('/login', methods=['GET', 'POST'])
def login():
from werkzeug.security import generate_password_hash
error = None
user = User(current_app.config['HTTP_BASIC_AUTH_USERNAME'], generate_password_hash(current_app.config['HTTP_BASIC_AUTH_PASSWORD']))

Expand Down
11 changes: 11 additions & 0 deletions build-mobile.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#!/bin/bash
p4a apk --private /app \
--package=com.bread.bucket \
--name "Bread Bucket Mobile" \
--version 0.1 \
--bootstrap=webview \
--requirements=flask,Flask-WTF,WTForms,Flask-SQLAlchemy,SQLAlchemy \
--port=5000 \
--dist_name BreadBucketMobile \
--permission WRITE_EXTERNAL_STORAGE
mv /BreadBucketMobile*.apk /build/BreadBucketMobile.apk
Empty file added build/.gitkeep
Empty file.
4 changes: 4 additions & 0 deletions main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
from app import app

if __name__ == "__main__":
app.run()
9 changes: 8 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,14 @@
"start": "npm run css-watch",
"docker-build": "docker build . -t cmeury/bread-bucket:latest",
"docker-push": "docker push cmeury/bread-bucket:latest",
"publish": "npm run css-build && npm run docker-build && npm run docker-push"
"publish": "npm run css-build && npm run docker-build && npm run docker-push",
"docker-build-mobile": "docker build -f MobileDockerfile . -t bread-bucket-apk",
"docker-build-mobile-apk": "docker run -v ~/Source/bread-bucket/build:/build:rw bread-bucket-apk",
"docker-full-mobile-build": "npm run docker-build-mobile && npm run docker-build-mobile-apk",
"docker-connect": "docker run -it -v ~/Source/bread-bucket/build:/build:rw --entrypoint bash bread-bucket-apk",
"install-apk": "adb install ./build/*.apk",
"emulator": "~/Android/Sdk/emulator/emulator -avd Pixel_3a_API_30 -netdelay none -netspeed full",
"emulator-logs": "adb logcat | grep python"
},
"author": "",
"license": "ISC",
Expand Down