def send_mail(email, type, data): if is_dev(): return if type == 'forgot-password': subject = "Gnothi reset password" body = f"Click this link to reset your password: {url}/reset-password?token={data}" elif type == 'welcome': subject = "Welcome to Gnothi" body = f"Thank you for registering a Gnothi account! Start journaling away." elif type == 'action': subject = f"Gnothi: {data['category']}/{data['action']}" body = subject else: return ses = boto3.client('ses') ses.send_email(Source=vars.EMAIL_SES_EMAIL_SOURCE, Destination={'ToAddresses': [email]}, Message={ 'Subject': { 'Data': subject }, 'Body': { 'Text': { 'Data': body } } })
def cloud_down_maybe(sess): if is_dev(): return # 15 minutes since last job active = M.Job.last_job(sess) < 15 if active or vars.MACHINE in ['desktop', 'laptop']: return sess.query(M.Machine).get(vars.MACHINE).delete() sess.commit() exit(0)
def cloud_down_maybe(sess): if is_dev(): return # 15 minutes since last user checkin active = M.User.last_checkin(sess) < 15 if active or vars.MACHINE in ['desktop', 'laptop']: return sess.query(M.Machine).filter_by(id=vars.MACHINE).delete() sess.commit() exit(0)
def cloud_up_maybe(): if is_dev(): return with session() as sess: if M.User.last_checkin(sess) > 15: return if M.Machine.gpu_status(sess) in ("on", "pending"): return logger.warning("Initing AWS Batch") M.Machine.notify_online(sess, 'batch', 'pending') boto3.client('batch').submit_job( jobName=str(uuid4()), jobQueue='gnothi-jq', jobDefinition='gnothi-jd', )
def cloud_up_maybe(): if is_dev(): return with session() as sess: if M.User.last_checkin(sess) > 10: return if M.Machine.gpu_status(sess) != "off": return logger.warning("Initing Paperspace") M.Machine.notify_online(sess, 'paperspace', 'pending') jobs = job_client.list() if any([j.state in up_states for j in jobs]): return vars_ = {**dict(vars), **{'MACHINE': 'paperspace'}} return job_client.create(machine_type='K80', container='lefnire/gnothi:gpu-0.0.13', project_id=vars.PAPERSPACE_PROJECT_ID, is_preemptible=True, command='python app/run.py', env_vars=vars_)
def send_mail(email, type, data): if is_dev(): return if type == 'forgot-password': body = f"Click this link to confirm your account: {url}/reset-password?token={data}" elif type == 'welcome': body = f"Thank you for registering a Gnothi account! Start journaling away." else: return ses = boto3.client('ses') ses.send_email( Source=vars.EMAIL_SES_EMAIL_SOURCE, Destination={'ToAddresses': [email]}, Message={ 'Subject': {'Data': 'Confirm Your Account'}, 'Body': { 'Text': {'Data': body} } } )
from app.app_app import app as app_ import app.app_jwt import app.app_routes from common.utils import is_dev app = app_ if __name__ == "__main__": args_ = {'debug': True} if is_dev() else {'port': 80} app.run(host='0.0.0.0', **args_)
def sync_for(user): if is_dev(): return if not (user.habitica_user_id and user.habitica_api_token): return # https://habitica.com/apidoc/#api-Task-GetUserTasks logger.info("Calling Habitica") headers = { "Content-Type": "application/json", "x-api-user": user.habitica_user_id, "x-api-key": user.habitica_api_token, "x-client": f"{vars.HABIT_USER}-{vars.HABIT_APP}" } tasks = requests.get('https://habitica.com/api/v3/tasks/user', headers=headers).json()['data'] huser = requests.get( 'https://habitica.com/api/v3/user?userFields=lastCron,needsCron,preferences', headers=headers).json()['data'] # don't pull field if they're in the inn if huser['preferences']['sleep']: return # Use SQL to determine day, so not managing timezones in python + sql tz = M.User.tz(db.session, user.id) last_cron = db.session.execute( text(""" select date(:lastCron ::timestamptz at time zone :tz)::text last_cron """), dict(lastCron=huser['lastCron'], tz=tz)).fetchone().last_cron f_map = {f.service_id: f for f in user.fields} t_map = {task['id']: task for task in tasks} # Remove Habitica-deleted tasks for f in user.fields: if f.service != 'habitica': continue if f.service_id not in t_map: db.session.delete(f) db.session.commit() # Add/update tasks from Habitica for task in tasks: # {id, text, type, value} # habit: {counterUp, counterDown} # daily:{checklist: [{completed}], completed, isDue} # only care about habits/dailies if task['type'] not in ['habit', 'daily']: continue f = f_map.get(task['id'], None) if not f: # Field doesn't exist here yet, add it. # TODO delete things here if deleted in habitica f = M.Field(service='habitica', service_id=task['id'], name=task['text'], type='number') user.fields.append(f) # Text has changed on Habitica, update here if f.name != task['text']: f.name = task['text'] db.session.commit() # for f to have f.id value = 0. # Habit if task['type'] == 'habit': value = (task['counterUp'] or 0.) - (task['counterDown'] or 0.) # Daily else: value = 1. if task['completed'] \ else 0. if not task['isDue'] \ else -1. # With Checklist cl = task['checklist'] if (not task['completed']) and any(c['completed'] for c in cl): value = sum(c['completed'] for c in cl) / len(cl) M.FieldEntry.upsert(db.session, user_id=user.id, field_id=f.id, value=value, day=last_cron) M.Field.update_avg(f.id) logger.info(task['text'] + " done")
def render(self, template_path, extra_context={}, output='html'): """render(template_path, extra_context={}, output='html') -> None Render the given template with the extra (optional) values and write it to the response. Args: template_path str, list - The template to render. extra_context dict - Template values to provide to the template. output str - The outputed page type. Only supports 'html' Return: None """ output_info = const.OUTPUT_TYPES[output] parsed = urlparse.urlparse(self.request.url) get = self.request.get user = self.user udata = self.udata # We want to force the user to give themself a nickname # But it will fall into an infinate loop before the user can submit a name. if udata and not udata.nickname and not parsed.path.startswith('/first/'): self.redirect('/first/?continue=%s' % self.url) return cf_credit = '' cf_script = get('cf_script', '') use_custom_cf = False if cf_script: pass elif udata: if udata.use_custom_cf: use_custom_cf = True cf_script = udata.custom_cf_script elif udata.cf_script != 'disabled': cf_script = udata.cf_script else: cf_script = 'daze' if not use_custom_cf: path = os.path.join('.', 'data', 'contextfree', '%s.txt' % cf_script.lower()) if cf_script and os.path.exists(path): with open(path) as f: cf_credit = f.readline() cf_script = f.read() msg_count = None if udata: msg_count = counter.Counter('%sMessages' % udata.key_name).count # Allow forced message overrides msg = get('msg') if msg: self.flash.msg = msg context = { 'cf_credit' : cf_credit, 'cf_script' : cf_script, 'debug' : get('debug'), 'flash' : self.flash, 'host_url' : "%s://%s" % (parsed.scheme, parsed.hostname), 'is_dev' : utils.is_dev(), 'login_url' : users.create_login_url('/first/?continue=%s' % self.url), 'logout_url': users.create_logout_url('/'), 'msg_count' : msg_count, 'page_admin': users.is_current_user_admin(), 'request' : self.request, 'design' : get('design'), 'theme' : get('theme'), 'udata' : udata, 'url' : self.url, 'user' : user, 'year' : datetime.datetime.now().year } context.update(extra_context) context.update(self.context) if get('json', False) is False: renderer = TemplateRenderer(template_path, context, output) rendered = renderer.render() else: const.OUTPUT_TYPES['json'] import simplejson rendered = simplejson.dumps(context, skipkeys=True).replace('\\n', '\n') self.response.headers['Content-Type'] = output_info['header'] self.response.headers['Content-Length'] = str(len(rendered)) self.response.out.write(rendered)
from common.utils import vars, is_dev import boto3 url = "http://localhost:3002" if is_dev() else "https://gnothiai.com" def send_mail(email, type, data): if is_dev(): return if type == 'forgot-password': subject = "Gnothi reset password" body = f"Click this link to reset your password: {url}/reset-password?token={data}" elif type == 'welcome': subject = "Welcome to Gnothi" body = f"Thank you for registering a Gnothi account! Start journaling away." elif type == 'action': subject = f"Gnothi: {data['category']}/{data['action']}" body = subject else: return ses = boto3.client('ses') ses.send_email(Source=vars.EMAIL_SES_EMAIL_SOURCE, Destination={'ToAddresses': [email]}, Message={ 'Subject': { 'Data': subject }, 'Body': { 'Text': { 'Data': body }
import requests from common.utils import vars, is_dev from typing import Union from pydantic import UUID4 import hashlib DEBUG = is_dev() def ga(uid: Union[str, UUID4], category: str, action: str): """ I'm only tracking interesting server-side events right now (no cookies), I want to see what features are being used and important bits like sign-ups & book-thumbs. user-id is obfuscated https://developers.google.com/analytics/devguides/collection/protocol/v1/devguide#event """ # actually don't care about uid, just need a unique identifier. Note this is # a 1-way hash (right?), so I can't even decrypt - just want unique per-feature track uid = str(uid).encode() # to bytes uid = hashlib.sha256(uid).hexdigest() url = "https://ssl.google-analytics.com/" url += "debug/collect" if DEBUG else "collect" res = requests.post(url, params=dict( v=1, tid=vars.GA, cid=uid, t='event', ec=category, ea=action )) if DEBUG: print(res.json())
def sync_for(user): if is_dev(): return if not (user.habitica_user_id and user.habitica_api_token): return # https://habitica.com/apidoc/#api-Task-GetUserTasks logger.info("Calling Habitica") headers = { "Content-Type": "application/json", "x-api-user": user.habitica_user_id, "x-api-key": user.habitica_api_token, "x-client": f"{vars.HABIT_USER}-{vars.HABIT_APP}" } tasks = requests.get('https://habitica.com/api/v3/tasks/user', headers=headers).json()['data'] huser = requests.get( 'https://habitica.com/api/v3/user?userFields=lastCron,needsCron', headers=headers).json()['data'] lastCron = dparse(huser['lastCron']) logger.info("Habitica finished") fes = M.FieldEntry.get_day_entries(user.id, day=lastCron).all() f_map = {f.service_id: f for f in user.fields} fe_map = {fe.field_id: fe for fe in fes} t_map = {task['id']: task for task in tasks} # Remove Habitica-deleted tasks for f in user.fields: if f.service != 'habitica': continue if f.service_id not in t_map: db.session.delete(f) db.session.commit() # Add/update tasks from Habitica for task in tasks: # {id, text, type, value} # habit: {counterUp, counterDown} # daily:{checklist: [{completed}], completed, isDue} # only care about habits/dailies if task['type'] not in ['habit', 'daily']: continue f = f_map.get(task['id'], None) if not f: # Field doesn't exist here yet, add it. # TODO delete things here if deleted in habitica f = M.Field(service='habitica', service_id=task['id'], name=task['text'], type='number') user.fields.append(f) # Text has changed on Habitica, update here if f.name != task['text']: f.name = task['text'] db.session.commit() # for f to have f.id value = 0. # Habit if task['type'] == 'habit': value = (task['counterUp'] or 0.) - (task['counterDown'] or 0.) # Daily else: value = 1. if task['completed'] \ else 0. if not task['isDue'] \ else -1. # With Checklist cl = task['checklist'] if (not task['completed']) and any(c['completed'] for c in cl): value = sum(c['completed'] for c in cl) / len(cl) fe = fe_map.get(f.id, None) if fe: fe.value = value else: fe = M.FieldEntry(field_id=f.id, created_at=lastCron, value=value) user.field_entries.append(fe) db.session.commit() logger.info(task['text'] + " done")