Run reformatter on all files (#98)
This commit is contained in:
parent
8f7fa45bb6
commit
c51e850cb0
|
@ -1,2 +1,2 @@
|
|||
[settings]
|
||||
line_length=120
|
||||
line_length = 120
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
repos:
|
||||
- repo: https://github.com/ambv/black
|
||||
- repo: https://github.com/ambv/black
|
||||
rev: stable
|
||||
hooks:
|
||||
- id: black
|
||||
language_version: python3.6
|
||||
- id: black
|
||||
language_version: python3.6
|
||||
|
|
|
@ -5,6 +5,7 @@ from grant.blockchain.bootstrap import send_bootstrap_data
|
|||
|
||||
app = create_app()
|
||||
|
||||
|
||||
@app.before_first_request
|
||||
def bootstrap_watcher():
|
||||
send_bootstrap_data()
|
||||
send_bootstrap_data()
|
||||
|
|
|
@ -1,2 +1,2 @@
|
|||
from . import views
|
||||
from . import commands
|
||||
from . import views
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
import click
|
||||
import getpass
|
||||
from flask.cli import with_appcontext
|
||||
|
||||
from grant.utils.admin import generate_admin_password_hash
|
||||
import click
|
||||
from grant.settings import SECRET_KEY
|
||||
from grant.utils.admin import generate_admin_password_hash
|
||||
|
||||
|
||||
@click.command()
|
||||
|
|
|
@ -5,6 +5,7 @@ class FakeUser(object):
|
|||
display_name = 'Example User'
|
||||
title = 'Email Example Dude'
|
||||
|
||||
|
||||
class FakeProposal(object):
|
||||
id = 123
|
||||
title = 'Example proposal'
|
||||
|
@ -12,18 +13,21 @@ class FakeProposal(object):
|
|||
content = 'Example example example example'
|
||||
target = "100"
|
||||
|
||||
|
||||
class FakeContribution(object):
|
||||
id = 123
|
||||
amount = '12.5'
|
||||
proposal_id = 123
|
||||
user_id = 123
|
||||
|
||||
|
||||
class FakeUpdate(object):
|
||||
id = 123
|
||||
title = 'Example update'
|
||||
content = 'Example example example example\n\nExample example example example'
|
||||
proposal_id = 123
|
||||
|
||||
|
||||
user = FakeUser()
|
||||
proposal = FakeProposal()
|
||||
contribution = FakeContribution()
|
||||
|
|
|
@ -1,13 +1,9 @@
|
|||
from functools import wraps
|
||||
from flask import Blueprint, request
|
||||
from flask import Blueprint, request
|
||||
from flask_yoloapi import endpoint, parameter
|
||||
from hashlib import sha256
|
||||
from uuid import uuid4
|
||||
from sqlalchemy import func, or_
|
||||
|
||||
from grant.comment.models import Comment, user_comments_schema
|
||||
from grant.email.send import generate_email
|
||||
from grant.extensions import db
|
||||
from grant.utils.admin import admin_auth_required, admin_is_authed, admin_login, admin_logout
|
||||
from grant.user.models import User, users_schema, user_schema
|
||||
from grant.proposal.models import (
|
||||
Proposal,
|
||||
ProposalContribution,
|
||||
|
@ -16,10 +12,11 @@ from grant.proposal.models import (
|
|||
user_proposal_contributions_schema,
|
||||
PENDING
|
||||
)
|
||||
from grant.comment.models import Comment, comments_schema, user_comments_schema
|
||||
from grant.email.send import generate_email
|
||||
from .example_emails import example_email_args
|
||||
from grant.user.models import User, users_schema, user_schema
|
||||
from grant.utils.admin import admin_auth_required, admin_is_authed, admin_login, admin_logout
|
||||
from sqlalchemy import func, or_
|
||||
|
||||
from .example_emails import example_email_args
|
||||
|
||||
blueprint = Blueprint('admin', __name__, url_prefix='/api/v1/admin')
|
||||
|
||||
|
|
|
@ -1,16 +1,14 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
"""The app module, containing the app factory function."""
|
||||
import sentry_sdk
|
||||
from flask import Flask
|
||||
from flask_cors import CORS
|
||||
from flask_sslify import SSLify
|
||||
from flask_security import SQLAlchemyUserDatastore
|
||||
from sentry_sdk.integrations.flask import FlaskIntegration
|
||||
import sentry_sdk
|
||||
|
||||
from flask_sslify import SSLify
|
||||
from grant import commands, proposal, user, comment, milestone, admin, email, blockchain
|
||||
from grant.extensions import bcrypt, migrate, db, ma, security
|
||||
from grant.settings import SENTRY_RELEASE, ENV
|
||||
from grant.blockchain.bootstrap import send_bootstrap_data
|
||||
from sentry_sdk.integrations.flask import FlaskIntegration
|
||||
|
||||
|
||||
def create_app(config_objects=["grant.settings"]):
|
||||
|
|
|
@ -1,31 +1,33 @@
|
|||
from grant.utils.requests import blockchain_post
|
||||
from grant.proposal.models import (
|
||||
ProposalContribution,
|
||||
proposal_contributions_schema,
|
||||
PENDING,
|
||||
CONFIRMED,
|
||||
)
|
||||
)
|
||||
from grant.utils.requests import blockchain_post
|
||||
|
||||
|
||||
def make_bootstrap_data():
|
||||
pending_contributions = ProposalContribution.query \
|
||||
.filter_by(status=PENDING) \
|
||||
.all()
|
||||
latest_contribution = ProposalContribution.query\
|
||||
.filter_by(status=CONFIRMED) \
|
||||
.order_by(ProposalContribution.date_created.desc()) \
|
||||
.first()
|
||||
return {
|
||||
"pendingContributions": proposal_contributions_schema.dump(pending_contributions),
|
||||
"latestTxId": latest_contribution.tx_id if latest_contribution else None,
|
||||
}
|
||||
pending_contributions = ProposalContribution.query \
|
||||
.filter_by(status=PENDING) \
|
||||
.all()
|
||||
latest_contribution = ProposalContribution.query \
|
||||
.filter_by(status=CONFIRMED) \
|
||||
.order_by(ProposalContribution.date_created.desc()) \
|
||||
.first()
|
||||
return {
|
||||
"pendingContributions": proposal_contributions_schema.dump(pending_contributions),
|
||||
"latestTxId": latest_contribution.tx_id if latest_contribution else None,
|
||||
}
|
||||
|
||||
|
||||
def send_bootstrap_data():
|
||||
data = make_bootstrap_data()
|
||||
print('Sending bootstrap data to blockchain watcher microservice')
|
||||
print(' * Latest transaction ID: {}'.format(data['latestTxId']))
|
||||
print(' * Number of pending contributions: {}'.format(len(data['pendingContributions'])))
|
||||
data = make_bootstrap_data()
|
||||
print('Sending bootstrap data to blockchain watcher microservice')
|
||||
print(' * Latest transaction ID: {}'.format(data['latestTxId']))
|
||||
print(' * Number of pending contributions: {}'.format(len(data['pendingContributions'])))
|
||||
|
||||
res = blockchain_post('/bootstrap', data)
|
||||
print('Blockchain watcher has started')
|
||||
print('Starting chain height: {}'.format(res['startHeight']))
|
||||
print('Current chain height: {}'.format(res['currentHeight']))
|
||||
res = blockchain_post('/bootstrap', data)
|
||||
print('Blockchain watcher has started')
|
||||
print('Starting chain height: {}'.format(res['startHeight']))
|
||||
print('Current chain height: {}'.format(res['currentHeight']))
|
||||
|
|
|
@ -1,15 +1,15 @@
|
|||
from flask import Blueprint, g
|
||||
from flask import Blueprint
|
||||
from flask_yoloapi import endpoint
|
||||
|
||||
from grant.utils.auth import internal_webhook
|
||||
from grant.blockchain.bootstrap import send_bootstrap_data
|
||||
from grant.utils.auth import internal_webhook
|
||||
|
||||
blueprint = Blueprint("blockchain", __name__, url_prefix="/api/v1/blockchain")
|
||||
|
||||
|
||||
@blueprint.route("/bootstrap", methods=["GET"])
|
||||
@internal_webhook
|
||||
@endpoint.api()
|
||||
def get_bootstrap_info():
|
||||
print('Bootstrap data requested from blockchain watcher microservice...')
|
||||
send_bootstrap_data()
|
||||
return True
|
||||
print('Bootstrap data requested from blockchain watcher microservice...')
|
||||
send_bootstrap_data()
|
||||
return True
|
||||
|
|
|
@ -1,2 +1,2 @@
|
|||
from . import views
|
||||
from . import models
|
||||
from . import views
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import datetime
|
||||
from sqlalchemy.orm import raiseload
|
||||
|
||||
from grant.extensions import ma, db
|
||||
from grant.utils.misc import dt_to_unix
|
||||
from sqlalchemy.orm import raiseload
|
||||
|
||||
|
||||
class Comment(db.Model):
|
||||
|
@ -70,6 +70,7 @@ class UserCommentSchema(ma.Schema):
|
|||
"content",
|
||||
"date_created",
|
||||
)
|
||||
|
||||
proposal = ma.Nested(
|
||||
"ProposalSchema",
|
||||
exclude=[
|
||||
|
|
|
@ -1,2 +1,2 @@
|
|||
from . import views
|
||||
from . import models
|
||||
from . import views
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
from datetime import timedelta
|
||||
from datetime import datetime
|
||||
from datetime import timedelta
|
||||
|
||||
from grant.extensions import ma, db
|
||||
from grant.utils.misc import gen_random_code
|
||||
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
from flask import render_template, Markup, current_app
|
||||
|
||||
import sendgrid
|
||||
from sendgrid.helpers.mail import Email, Mail, Content
|
||||
from flask import render_template, Markup, current_app
|
||||
from grant.settings import SENDGRID_API_KEY, SENDGRID_DEFAULT_FROM
|
||||
from python_http_client import HTTPError
|
||||
from sendgrid.helpers.mail import Email, Mail, Content
|
||||
|
||||
from .subscription_settings import EmailSubscription, is_subscribed
|
||||
|
||||
default_template_args = {
|
||||
|
@ -55,6 +55,7 @@ def proposal_rejected(email_args):
|
|||
'subscription': EmailSubscription.MY_PROPOSAL_APPROVAL
|
||||
}
|
||||
|
||||
|
||||
def proposal_contribution(email_args):
|
||||
return {
|
||||
'subject': 'You just got a contribution!',
|
||||
|
@ -67,6 +68,7 @@ def proposal_contribution(email_args):
|
|||
'subscription': EmailSubscription.MY_PROPOSAL_FUNDED,
|
||||
}
|
||||
|
||||
|
||||
def proposal_comment(email_args):
|
||||
return {
|
||||
'subject': 'New comment from {}'.format(email_args['author'].display_name),
|
||||
|
@ -78,6 +80,7 @@ def proposal_comment(email_args):
|
|||
'subscription': EmailSubscription.MY_PROPOSAL_COMMENT,
|
||||
}
|
||||
|
||||
|
||||
def contribution_confirmed(email_args):
|
||||
return {
|
||||
'subject': 'Your contribution has been confirmed!',
|
||||
|
@ -89,6 +92,7 @@ def contribution_confirmed(email_args):
|
|||
'subscription': EmailSubscription.FUNDED_PROPOSAL_CONTRIBUTION,
|
||||
}
|
||||
|
||||
|
||||
def contribution_update(email_args):
|
||||
return {
|
||||
'subject': 'The {} team posted an update'.format(email_args['proposal'].title),
|
||||
|
@ -100,6 +104,7 @@ def contribution_update(email_args):
|
|||
'subscription': EmailSubscription.FUNDED_PROPOSAL_UPDATE,
|
||||
}
|
||||
|
||||
|
||||
def comment_reply(email_args):
|
||||
return {
|
||||
'subject': 'New reply from {}'.format(email_args['author'].display_name),
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
from enum import Enum
|
||||
|
||||
from grant.utils.exceptions import ValidationException
|
||||
|
||||
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
from flask import Blueprint
|
||||
from flask_yoloapi import endpoint, parameter
|
||||
|
||||
from .models import EmailVerification, EmailRecovery, db
|
||||
from flask_yoloapi import endpoint
|
||||
|
||||
from .models import EmailVerification, db
|
||||
|
||||
blueprint = Blueprint("email", __name__, url_prefix="/api/v1/email")
|
||||
|
||||
|
|
|
@ -3,8 +3,8 @@
|
|||
from flask_bcrypt import Bcrypt
|
||||
from flask_marshmallow import Marshmallow
|
||||
from flask_migrate import Migrate
|
||||
from flask_sqlalchemy import SQLAlchemy
|
||||
from flask_security import Security
|
||||
from flask_sqlalchemy import SQLAlchemy
|
||||
|
||||
bcrypt = Bcrypt()
|
||||
db = SQLAlchemy()
|
||||
|
|
|
@ -1,2 +1,2 @@
|
|||
from . import views
|
||||
from . import models
|
||||
from . import views
|
||||
|
|
|
@ -43,7 +43,7 @@ class Milestone(db.Model):
|
|||
self.immediate_payout = immediate_payout
|
||||
self.proposal_id = proposal_id
|
||||
self.date_created = datetime.datetime.now()
|
||||
|
||||
|
||||
@staticmethod
|
||||
def validate(milestone):
|
||||
if len(milestone.title) > 60:
|
||||
|
|
|
@ -1,15 +1,13 @@
|
|||
import datetime
|
||||
from typing import List
|
||||
from sqlalchemy import func, or_
|
||||
from functools import reduce
|
||||
|
||||
from grant.comment.models import Comment
|
||||
from grant.extensions import ma, db
|
||||
from grant.utils.misc import dt_to_unix, make_url
|
||||
from grant.utils.exceptions import ValidationException
|
||||
from grant.utils.requests import blockchain_get
|
||||
from grant.email.send import send_email
|
||||
|
||||
from grant.extensions import ma, db
|
||||
from grant.utils.exceptions import ValidationException
|
||||
from grant.utils.misc import dt_to_unix, make_url
|
||||
from grant.utils.requests import blockchain_get
|
||||
from sqlalchemy import func, or_
|
||||
|
||||
# Proposal states
|
||||
DRAFT = 'DRAFT'
|
||||
|
@ -101,10 +99,10 @@ class ProposalContribution(db.Model):
|
|||
user = db.relationship("User")
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
proposal_id: int,
|
||||
user_id: int,
|
||||
amount: str
|
||||
self,
|
||||
proposal_id: int,
|
||||
user_id: int,
|
||||
amount: str
|
||||
):
|
||||
self.proposal_id = proposal_id
|
||||
self.user_id = user_id
|
||||
|
@ -243,14 +241,14 @@ class Proposal(db.Model):
|
|||
.all()
|
||||
|
||||
def update(
|
||||
self,
|
||||
title: str = '',
|
||||
brief: str = '',
|
||||
category: str = '',
|
||||
content: str = '',
|
||||
target: str = '0',
|
||||
payout_address: str = '',
|
||||
deadline_duration: int = 5184000 # 60 days
|
||||
self,
|
||||
title: str = '',
|
||||
brief: str = '',
|
||||
category: str = '',
|
||||
content: str = '',
|
||||
target: str = '0',
|
||||
payout_address: str = '',
|
||||
deadline_duration: int = 5184000 # 60 days
|
||||
):
|
||||
self.title = title
|
||||
self.brief = brief
|
||||
|
@ -439,6 +437,7 @@ class ProposalTeamInviteSchema(ma.Schema):
|
|||
proposal_team_invite_schema = ProposalTeamInviteSchema()
|
||||
proposal_team_invites_schema = ProposalTeamInviteSchema(many=True)
|
||||
|
||||
|
||||
# TODO: Find a way to extend ProposalTeamInviteSchema instead of redefining
|
||||
|
||||
|
||||
|
|
|
@ -1,19 +1,15 @@
|
|||
from dateutil.parser import parse
|
||||
from functools import wraps
|
||||
import ast
|
||||
|
||||
from flask import Blueprint, g
|
||||
from flask_yoloapi import endpoint, parameter
|
||||
from sqlalchemy.exc import IntegrityError
|
||||
from sqlalchemy import or_
|
||||
|
||||
from grant.comment.models import Comment, comment_schema, comments_schema
|
||||
from grant.milestone.models import Milestone
|
||||
from grant.user.models import User, SocialMedia, Avatar
|
||||
from grant.email.send import send_email
|
||||
from grant.milestone.models import Milestone
|
||||
from grant.user.models import User
|
||||
from grant.utils.auth import requires_auth, requires_team_member_auth, get_authed_user, internal_webhook
|
||||
from grant.utils.exceptions import ValidationException
|
||||
from grant.utils.misc import is_email, make_url, from_zat, make_preview
|
||||
from sqlalchemy import or_
|
||||
|
||||
from .models import (
|
||||
Proposal,
|
||||
proposals_schema,
|
||||
|
@ -35,7 +31,6 @@ from .models import (
|
|||
DELETED,
|
||||
CONFIRMED,
|
||||
)
|
||||
import traceback
|
||||
|
||||
blueprint = Blueprint("proposal", __name__, url_prefix="/api/v1/proposals")
|
||||
|
||||
|
@ -136,14 +131,14 @@ def get_proposals(stage):
|
|||
if stage:
|
||||
proposals = (
|
||||
Proposal.query.filter_by(status=LIVE, stage=stage)
|
||||
.order_by(Proposal.date_created.desc())
|
||||
.all()
|
||||
.order_by(Proposal.date_created.desc())
|
||||
.all()
|
||||
)
|
||||
else:
|
||||
proposals = (
|
||||
Proposal.query.filter_by(status=LIVE)
|
||||
.order_by(Proposal.date_created.desc())
|
||||
.all()
|
||||
.order_by(Proposal.date_created.desc())
|
||||
.all()
|
||||
)
|
||||
dumped_proposals = proposals_schema.dump(proposals)
|
||||
return dumped_proposals
|
||||
|
@ -170,11 +165,11 @@ def make_proposal_draft():
|
|||
def get_proposal_drafts():
|
||||
proposals = (
|
||||
Proposal.query
|
||||
.filter(or_(Proposal.status == DRAFT, Proposal.status == REJECTED))
|
||||
.join(proposal_team)
|
||||
.filter(proposal_team.c.user_id == g.current_user.id)
|
||||
.order_by(Proposal.date_created.desc())
|
||||
.all()
|
||||
.filter(or_(Proposal.status == DRAFT, Proposal.status == REJECTED))
|
||||
.join(proposal_team)
|
||||
.filter(proposal_team.c.user_id == g.current_user.id)
|
||||
.order_by(Proposal.date_created.desc())
|
||||
.all()
|
||||
)
|
||||
return proposals_schema.dump(proposals), 200
|
||||
|
||||
|
@ -367,7 +362,7 @@ def get_proposal_contributions(proposal_id):
|
|||
proposal = Proposal.query.filter_by(id=proposal_id).first()
|
||||
if not proposal:
|
||||
return {"message": "No proposal matching id"}, 404
|
||||
|
||||
|
||||
top_contributions = ProposalContribution.query \
|
||||
.filter_by(proposal_id=proposal_id, status=CONFIRMED) \
|
||||
.order_by(ProposalContribution.amount.desc()) \
|
||||
|
@ -378,12 +373,11 @@ def get_proposal_contributions(proposal_id):
|
|||
.order_by(ProposalContribution.date_created.desc()) \
|
||||
.limit(5) \
|
||||
.all()
|
||||
|
||||
|
||||
return {
|
||||
'top': proposal_proposal_contributions_schema.dump(top_contributions),
|
||||
'latest': proposal_proposal_contributions_schema.dump(latest_contributions),
|
||||
}
|
||||
|
||||
|
||||
|
||||
@blueprint.route("/<proposal_id>/contributions/<contribution_id>", methods=["GET"])
|
||||
|
@ -479,6 +473,7 @@ def post_contribution_confirmation(contribution_id, to, amount, txid):
|
|||
|
||||
return None, 200
|
||||
|
||||
|
||||
@blueprint.route("/contribution/<contribution_id>", methods=["DELETE"])
|
||||
@requires_auth
|
||||
@endpoint.api()
|
||||
|
|
|
@ -6,10 +6,8 @@ Most configuration is set via environment variables.
|
|||
For local development, use a .env file to set
|
||||
environment variables.
|
||||
"""
|
||||
import subprocess
|
||||
from environs import Env
|
||||
|
||||
|
||||
env = Env()
|
||||
env.read_env()
|
||||
|
||||
|
|
|
@ -1,25 +1,26 @@
|
|||
<p style="margin: 0 0 20px;">
|
||||
You just received a reply from
|
||||
<a href="{{ args.author_url }}" target="_blank">{{ args.author.display_name }}</a>
|
||||
on <strong>{{ args.proposal.title }}</strong>:
|
||||
You just received a reply from
|
||||
<a href="{{ args.author_url }}" target="_blank">{{ args.author.display_name }}</a>
|
||||
on <strong>{{ args.proposal.title }}</strong>:
|
||||
</p>
|
||||
|
||||
<p style="margin: 10px 0; padding: 20px; background: #F8F8F8;">
|
||||
“{{ args.preview }}”
|
||||
“{{ args.preview }}”
|
||||
</p>
|
||||
|
||||
<table width="100%" border="0" cellspacing="0" cellpadding="0">
|
||||
<tr>
|
||||
<td bgcolor="#ffffff" align="center" style="padding: 40px 30px 40px 30px;">
|
||||
<table border="0" cellspacing="0" cellpadding="0">
|
||||
<tr>
|
||||
<td align="center" style="border-radius: 3px;" bgcolor="#530EEC">
|
||||
<a href="{{ args.comment_url }}" target="_blank" style="font-size: 20px; font-family: Helvetica, Arial, sans-serif; color: #ffffff; text-decoration: none; color: #ffffff; text-decoration: none; padding: 20px 50px; border-radius: 4px; border: 1px solid #530EEC; display: inline-block;">
|
||||
View their comment
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
<td bgcolor="#ffffff" align="center" style="padding: 40px 30px 40px 30px;">
|
||||
<table border="0" cellspacing="0" cellpadding="0">
|
||||
<tr>
|
||||
<td align="center" style="border-radius: 3px;" bgcolor="#530EEC">
|
||||
<a href="{{ args.comment_url }}" target="_blank"
|
||||
style="font-size: 20px; font-family: Helvetica, Arial, sans-serif; color: #ffffff; text-decoration: none; color: #ffffff; text-decoration: none; padding: 20px 50px; border-radius: 4px; border: 1px solid #530EEC; display: inline-block;">
|
||||
View their comment
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</table>
|
|
@ -1,20 +1,20 @@
|
|||
<p style="margin: 0 0 20px;">
|
||||
Your <strong>{{ args.contribution.amount }} ZEC</strong> contribution has
|
||||
been confirmed! <strong>{{ args.proposal.title}}</strong> has been updated
|
||||
to reflect your funding, and your account will now show your contribution.
|
||||
You can view your transaction below:
|
||||
Your <strong>{{ args.contribution.amount }} ZEC</strong> contribution has
|
||||
been confirmed! <strong>{{ args.proposal.title}}</strong> has been updated
|
||||
to reflect your funding, and your account will now show your contribution.
|
||||
You can view your transaction below:
|
||||
</p>
|
||||
|
||||
<p style="margin: 0 0 20px;">
|
||||
<a
|
||||
href="{{ args.tx_explorer_url }}"
|
||||
target="_blank"
|
||||
rel="nofollow noopener"
|
||||
>
|
||||
{{ args.tx_explorer_url }}
|
||||
</a>
|
||||
<a
|
||||
href="{{ args.tx_explorer_url }}"
|
||||
target="_blank"
|
||||
rel="nofollow noopener"
|
||||
>
|
||||
{{ args.tx_explorer_url }}
|
||||
</a>
|
||||
</p>
|
||||
|
||||
<p style="margin: 0;">
|
||||
Thank you for your help in improving the ZCash ecosystem.
|
||||
Thank you for your help in improving the ZCash ecosystem.
|
||||
</p>
|
||||
|
|
|
@ -1,32 +1,32 @@
|
|||
<p style="margin: 0;">
|
||||
A proposal you follow, <strong>{{ args.proposal.title }}</strong>, has
|
||||
posted an update:
|
||||
A proposal you follow, <strong>{{ args.proposal.title }}</strong>, has
|
||||
posted an update:
|
||||
</p>
|
||||
|
||||
<div style="margin: 10px 0; padding: 20px; background: #F8F8F8;">
|
||||
<h2 style="margin: 0 0 20px 0; font-size: 20px; font-weight: bold;">
|
||||
{{ args.proposal_update.title }}
|
||||
</h2>
|
||||
<p style="margin: 0;">{{ args.preview }}</p>
|
||||
<h2 style="margin: 0 0 20px 0; font-size: 20px; font-weight: bold;">
|
||||
{{ args.proposal_update.title }}
|
||||
</h2>
|
||||
<p style="margin: 0;">{{ args.preview }}</p>
|
||||
</div>
|
||||
|
||||
<table width="100%" border="0" cellspacing="0" cellpadding="0">
|
||||
|
||||
<table width="100%" border="0" cellspacing="0" cellpadding="0">
|
||||
<tr>
|
||||
<td bgcolor="#ffffff" align="center" style="padding: 40px 30px 40px 30px;">
|
||||
<table border="0" cellspacing="0" cellpadding="0">
|
||||
<tr>
|
||||
<td align="center" style="border-radius: 3px;" bgcolor="#530EEC">
|
||||
<a
|
||||
href="{{ args.update_url }}"
|
||||
target="_blank"
|
||||
style="font-size: 20px; font-family: Helvetica, Arial, sans-serif; color: #ffffff; text-decoration: none; color: #ffffff; text-decoration: none; padding: 20px 50px; border-radius: 4px; border: 1px solid #530EEC; display: inline-block;"
|
||||
>
|
||||
View the full update
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
<td bgcolor="#ffffff" align="center" style="padding: 40px 30px 40px 30px;">
|
||||
<table border="0" cellspacing="0" cellpadding="0">
|
||||
<tr>
|
||||
<td align="center" style="border-radius: 3px;" bgcolor="#530EEC">
|
||||
<a
|
||||
href="{{ args.update_url }}"
|
||||
target="_blank"
|
||||
style="font-size: 20px; font-family: Helvetica, Arial, sans-serif; color: #ffffff; text-decoration: none; color: #ffffff; text-decoration: none; padding: 20px 50px; border-radius: 4px; border: 1px solid #530EEC; display: inline-block;"
|
||||
>
|
||||
View the full update
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</table>
|
||||
|
|
@ -1,34 +1,34 @@
|
|||
<p style="margin: 0;">
|
||||
Congratulations on your approval! We look forward to seeing the support your
|
||||
proposal receives. To get your campaign started, click below and follow the
|
||||
instructions to publish your proposal.
|
||||
Congratulations on your approval! We look forward to seeing the support your
|
||||
proposal receives. To get your campaign started, click below and follow the
|
||||
instructions to publish your proposal.
|
||||
</p>
|
||||
|
||||
{% if args.admin_note %}
|
||||
<p style="margin: 20px 0 0;">
|
||||
A note from the admin team was attached to your approval:
|
||||
A note from the admin team was attached to your approval:
|
||||
</p>
|
||||
<p style="margin: 10px 0; padding: 20px; background: #F8F8F8;">
|
||||
“{{ args.admin_note }}”
|
||||
“{{ args.admin_note }}”
|
||||
</p>
|
||||
{% endif %}
|
||||
|
||||
<table width="100%" border="0" cellspacing="0" cellpadding="0">
|
||||
<tr>
|
||||
<td bgcolor="#ffffff" align="center" style="padding: 40px 30px 40px 30px;">
|
||||
<table border="0" cellspacing="0" cellpadding="0">
|
||||
<tr>
|
||||
<td align="center" style="border-radius: 3px;" bgcolor="#530EEC">
|
||||
<a
|
||||
href="{{ args.proposal_url }}"
|
||||
target="_blank"
|
||||
style="font-size: 20px; font-family: Helvetica, Arial, sans-serif; color: #ffffff; text-decoration: none; color: #ffffff; text-decoration: none; padding: 20px 50px; border-radius: 4px; border: 1px solid #530EEC; display: inline-block;"
|
||||
>
|
||||
Publish your proposal
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td bgcolor="#ffffff" align="center" style="padding: 40px 30px 40px 30px;">
|
||||
<table border="0" cellspacing="0" cellpadding="0">
|
||||
<tr>
|
||||
<td align="center" style="border-radius: 3px;" bgcolor="#530EEC">
|
||||
<a
|
||||
href="{{ args.proposal_url }}"
|
||||
target="_blank"
|
||||
style="font-size: 20px; font-family: Helvetica, Arial, sans-serif; color: #ffffff; text-decoration: none; color: #ffffff; text-decoration: none; padding: 20px 50px; border-radius: 4px; border: 1px solid #530EEC; display: inline-block;"
|
||||
>
|
||||
Publish your proposal
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
|
@ -1,24 +1,25 @@
|
|||
<p style="margin: 0 0 20px;">
|
||||
Your proposal <strong>{{ args.proposal.title }}</strong> just received a comment from
|
||||
<a href="{{ args.author_url }}" target="_blank">{{ args.author.display_name }}</a>:
|
||||
</p>
|
||||
</p>
|
||||
|
||||
<p style="margin: 10px 0; padding: 20px; background: #F8F8F8;">
|
||||
<p style="margin: 10px 0; padding: 20px; background: #F8F8F8;">
|
||||
“{{ args.preview }}”
|
||||
</p>
|
||||
</p>
|
||||
|
||||
<table width="100%" border="0" cellspacing="0" cellpadding="0">
|
||||
<tr>
|
||||
<table width="100%" border="0" cellspacing="0" cellpadding="0">
|
||||
<tr>
|
||||
<td bgcolor="#ffffff" align="center" style="padding: 40px 30px 40px 30px;">
|
||||
<table border="0" cellspacing="0" cellpadding="0">
|
||||
<tr>
|
||||
<td align="center" style="border-radius: 3px;" bgcolor="#530EEC">
|
||||
<a href="{{ args.comment_url }}" target="_blank" style="font-size: 20px; font-family: Helvetica, Arial, sans-serif; color: #ffffff; text-decoration: none; color: #ffffff; text-decoration: none; padding: 20px 50px; border-radius: 4px; border: 1px solid #530EEC; display: inline-block;">
|
||||
View their comment
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<table border="0" cellspacing="0" cellpadding="0">
|
||||
<tr>
|
||||
<td align="center" style="border-radius: 3px;" bgcolor="#530EEC">
|
||||
<a href="{{ args.comment_url }}" target="_blank"
|
||||
style="font-size: 20px; font-family: Helvetica, Arial, sans-serif; color: #ffffff; text-decoration: none; color: #ffffff; text-decoration: none; padding: 20px 50px; border-radius: 4px; border: 1px solid #530EEC; display: inline-block;">
|
||||
View their comment
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</tr>
|
||||
</table>
|
|
@ -1,23 +1,24 @@
|
|||
<p style="margin: 0 0 20px;">
|
||||
Your proposal <strong>{{ args.proposal.title }}</strong> just got a
|
||||
<strong>{{ args.contribution.amount }} ZEC</strong> contribution from
|
||||
<a href="{{ args.contributor_url }}" target="_blank">{{ args.contributor.display_name }}</a>.
|
||||
Your proposal is now at
|
||||
<strong>{{ args.funded }} / {{ args.proposal.target }} ZEC</strong>.
|
||||
Your proposal <strong>{{ args.proposal.title }}</strong> just got a
|
||||
<strong>{{ args.contribution.amount }} ZEC</strong> contribution from
|
||||
<a href="{{ args.contributor_url }}" target="_blank">{{ args.contributor.display_name }}</a>.
|
||||
Your proposal is now at
|
||||
<strong>{{ args.funded }} / {{ args.proposal.target }} ZEC</strong>.
|
||||
</p>
|
||||
|
||||
<table width="100%" border="0" cellspacing="0" cellpadding="0">
|
||||
<tr>
|
||||
<td bgcolor="#ffffff" align="center" style="padding: 40px 30px 40px 30px;">
|
||||
<table border="0" cellspacing="0" cellpadding="0">
|
||||
<tr>
|
||||
<td align="center" style="border-radius: 3px;" bgcolor="#530EEC">
|
||||
<a href="{{ args.proposal_url }}" target="_blank" style="font-size: 20px; font-family: Helvetica, Arial, sans-serif; color: #ffffff; text-decoration: none; color: #ffffff; text-decoration: none; padding: 20px 50px; border-radius: 4px; border: 1px solid #530EEC; display: inline-block;">
|
||||
View your Proposal
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td bgcolor="#ffffff" align="center" style="padding: 40px 30px 40px 30px;">
|
||||
<table border="0" cellspacing="0" cellpadding="0">
|
||||
<tr>
|
||||
<td align="center" style="border-radius: 3px;" bgcolor="#530EEC">
|
||||
<a href="{{ args.proposal_url }}" target="_blank"
|
||||
style="font-size: 20px; font-family: Helvetica, Arial, sans-serif; color: #ffffff; text-decoration: none; color: #ffffff; text-decoration: none; padding: 20px 50px; border-radius: 4px; border: 1px solid #530EEC; display: inline-block;">
|
||||
View your Proposal
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
|
@ -1,19 +1,19 @@
|
|||
<p style="margin: 0;">
|
||||
Your proposal has unfortunately been rejected. You're free to modify it
|
||||
and try submitting again.
|
||||
Your proposal has unfortunately been rejected. You're free to modify it
|
||||
and try submitting again.
|
||||
</p>
|
||||
|
||||
{% if args.admin_note %}
|
||||
<p style="margin: 20px 0 0;">
|
||||
A note from the admin team was attached to your rejection:
|
||||
A note from the admin team was attached to your rejection:
|
||||
</p>
|
||||
<p style="margin: 10px 0; padding: 20px; background: #F8F8F8;">
|
||||
“{{ args.admin_note }}”
|
||||
“{{ args.admin_note }}”
|
||||
</p>
|
||||
{% endif %}
|
||||
|
||||
<p style="margin: 20px 0 0; font-size: 12px; line-height: 18px; color: #999; text-align: center;">
|
||||
Please note that repeated submissions without significant changes or with
|
||||
content that doesn't match the platform guidelines may result in a removal
|
||||
of your submission privileges.
|
||||
Please note that repeated submissions without significant changes or with
|
||||
content that doesn't match the platform guidelines may result in a removal
|
||||
of your submission privileges.
|
||||
</p>
|
|
@ -1,40 +1,40 @@
|
|||
<p style="margin: 0;">
|
||||
Please use the below link to reset your password. The link will expire in one
|
||||
hour.
|
||||
Please use the below link to reset your password. The link will expire in one
|
||||
hour.
|
||||
</p>
|
||||
|
||||
<table width="100%" border="0" cellspacing="0" cellpadding="0">
|
||||
<tr>
|
||||
<td bgcolor="#ffffff" align="center" style="padding: 40px 30px 40px 30px;">
|
||||
<table border="0" cellspacing="0" cellpadding="0">
|
||||
<tr>
|
||||
<td align="center" style="border-radius: 3px;" bgcolor="#530EEC">
|
||||
<a
|
||||
href="{{ args.recover_url }}"
|
||||
target="_blank"
|
||||
style="font-size: 20px; font-family: Helvetica, Arial, sans-serif; color: #ffffff; text-decoration: none; color: #ffffff; text-decoration: none; padding: 20px 50px; border-radius: 4px; border: 1px solid #530EEC; display: inline-block;"
|
||||
>
|
||||
Reset Password
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td bgcolor="#ffffff" align="center" style="padding: 40px 30px 40px 30px;">
|
||||
<table border="0" cellspacing="0" cellpadding="0">
|
||||
<tr>
|
||||
<td align="center" style="border-radius: 3px;" bgcolor="#530EEC">
|
||||
<a
|
||||
href="{{ args.recover_url }}"
|
||||
target="_blank"
|
||||
style="font-size: 20px; font-family: Helvetica, Arial, sans-serif; color: #ffffff; text-decoration: none; color: #ffffff; text-decoration: none; padding: 20px 50px; border-radius: 4px; border: 1px solid #530EEC; display: inline-block;"
|
||||
>
|
||||
Reset Password
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<p style="margin: 0 0 10px; font-size: 14px; text-align: center;">
|
||||
If that doesn't work, copy and paste the following link in your browser
|
||||
If that doesn't work, copy and paste the following link in your browser
|
||||
</p>
|
||||
|
||||
<p style="margin: 0 0 30px; font-size: 12px; text-align: center;">
|
||||
<a href="{{ args.recover_url }}" target="_blank" style="color: #530EEC;">{{
|
||||
args.recover_url
|
||||
}}</a>
|
||||
<a href="{{ args.recover_url }}" target="_blank" style="color: #530EEC;">{{
|
||||
args.recover_url
|
||||
}}</a>
|
||||
</p>
|
||||
|
||||
<p style="margin: 0; font-size: 10px; color: #AAA; text-align: center;">
|
||||
If you did not make this request, you can safely ignore this email. A password
|
||||
reset request can be made by anyone, and it does not indicate that your
|
||||
account is in any danger of being accessed by someone else.
|
||||
If you did not make this request, you can safely ignore this email. A password
|
||||
reset request can be made by anyone, and it does not indicate that your
|
||||
account is in any danger of being accessed by someone else.
|
||||
</p>
|
||||
|
|
|
@ -1,31 +1,32 @@
|
|||
<p style="margin: 0;">
|
||||
We're excited to have you get started. First, you need to confirm your email address. Just click the button below.
|
||||
We're excited to have you get started. First, you need to confirm your email address. Just click the button below.
|
||||
</p>
|
||||
|
||||
<table width="100%" border="0" cellspacing="0" cellpadding="0">
|
||||
<tr>
|
||||
<td bgcolor="#ffffff" align="center" style="padding: 40px 30px 40px 30px;">
|
||||
<table border="0" cellspacing="0" cellpadding="0">
|
||||
<tr>
|
||||
<td align="center" style="border-radius: 3px;" bgcolor="#530EEC">
|
||||
<a href="{{ args.confirm_url }}" target="_blank" style="font-size: 20px; font-family: Helvetica, Arial, sans-serif; color: #ffffff; text-decoration: none; color: #ffffff; text-decoration: none; padding: 20px 50px; border-radius: 4px; border: 1px solid #530EEC; display: inline-block;">
|
||||
Confirm Email
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td bgcolor="#ffffff" align="center" style="padding: 40px 30px 40px 30px;">
|
||||
<table border="0" cellspacing="0" cellpadding="0">
|
||||
<tr>
|
||||
<td align="center" style="border-radius: 3px;" bgcolor="#530EEC">
|
||||
<a href="{{ args.confirm_url }}" target="_blank"
|
||||
style="font-size: 20px; font-family: Helvetica, Arial, sans-serif; color: #ffffff; text-decoration: none; color: #ffffff; text-decoration: none; padding: 20px 50px; border-radius: 4px; border: 1px solid #530EEC; display: inline-block;">
|
||||
Confirm Email
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<p style="margin: 0 0 10px; font-size: 14px; text-align: center;">
|
||||
If that doesn't work, copy and paste the following link in your browser
|
||||
If that doesn't work, copy and paste the following link in your browser
|
||||
</p>
|
||||
|
||||
<p style="margin: 0 0 30px; font-size: 12px; text-align: center;">
|
||||
<a href="{{ args.confirm_url }}" target="_blank" style="color: #530EEC;">{{ args.confirm_url }}</a>
|
||||
<a href="{{ args.confirm_url }}" target="_blank" style="color: #530EEC;">{{ args.confirm_url }}</a>
|
||||
</p>
|
||||
|
||||
<p style="margin: 0; font-size: 10px; color: #AAA; text-align: center;">
|
||||
Don’t know why you got this email? Don’t worry, you can safely ignore it. We won’t send you anymore.
|
||||
Don’t know why you got this email? Don’t worry, you can safely ignore it. We won’t send you anymore.
|
||||
</p>
|
|
@ -1,34 +1,34 @@
|
|||
<p style="margin: 0;">
|
||||
You’ve been invited by <strong>{{ args.inviter.display_name }}</strong> to
|
||||
join the team for
|
||||
<strong>{{ args.proposal.title or '<em>Untitled Project</em>'|safe }}</strong
|
||||
>, a project on Grant.io! If you want to accept the invitation, continue to
|
||||
the site below.
|
||||
You’ve been invited by <strong>{{ args.inviter.display_name }}</strong> to
|
||||
join the team for
|
||||
<strong>{{ args.proposal.title or '<em>Untitled Project</em>'|safe }}</strong
|
||||
>, a project on Grant.io! If you want to accept the invitation, continue to
|
||||
the site below.
|
||||
</p>
|
||||
|
||||
{% if not args.user %}
|
||||
<p style="margin: 20px 0 0;">
|
||||
It looks like you don't yet have a Grant.io account, so you'll need to sign up
|
||||
first before you can join the team.
|
||||
It looks like you don't yet have a Grant.io account, so you'll need to sign up
|
||||
first before you can join the team.
|
||||
</p>
|
||||
{% endif %}
|
||||
|
||||
<table width="100%" border="0" cellspacing="0" cellpadding="0">
|
||||
<tr>
|
||||
<td bgcolor="#ffffff" align="center" style="padding: 40px 30px 0 30px;">
|
||||
<table border="0" cellspacing="0" cellpadding="0">
|
||||
<tr>
|
||||
<td align="center" style="border-radius: 3px;" bgcolor="#530EEC">
|
||||
<a
|
||||
href="{{ args.invite_url }}"
|
||||
target="_blank"
|
||||
style="font-size: 20px; font-family: Helvetica, Arial, sans-serif; color: #ffffff; text-decoration: none; color: #ffffff; text-decoration: none; padding: 20px 50px; border-radius: 4px; border: 1px solid #530EEC; display: inline-block;"
|
||||
>
|
||||
{% if args.user %} See invitation {% else %} Get started {% endif %}
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td bgcolor="#ffffff" align="center" style="padding: 40px 30px 0 30px;">
|
||||
<table border="0" cellspacing="0" cellpadding="0">
|
||||
<tr>
|
||||
<td align="center" style="border-radius: 3px;" bgcolor="#530EEC">
|
||||
<a
|
||||
href="{{ args.invite_url }}"
|
||||
target="_blank"
|
||||
style="font-size: 20px; font-family: Helvetica, Arial, sans-serif; color: #ffffff; text-decoration: none; color: #ffffff; text-decoration: none; padding: 20px 50px; border-radius: 4px; border: 1px solid #530EEC; display: inline-block;"
|
||||
>
|
||||
{% if args.user %} See invitation {% else %} Get started {% endif %}
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
|
|
@ -4,45 +4,72 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title></title>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||
<style type="text/css">
|
||||
/* FONTS */
|
||||
@import url('https://fonts.googleapis.com/css?family=Nunito+Sans');
|
||||
|
||||
/* CLIENT-SPECIFIC STYLES */
|
||||
body, table, td, a { -webkit-text-size-adjust: 100%; -ms-text-size-adjust: 100%; }
|
||||
table, td { mso-table-lspace: 0pt; mso-table-rspace: 0pt; }
|
||||
img { -ms-interpolation-mode: bicubic; }
|
||||
<title></title>
|
||||
<meta content="text/html; charset=utf-8" http-equiv="Content-Type"/>
|
||||
<meta content="width=device-width, initial-scale=1" name="viewport">
|
||||
<meta content="IE=edge" http-equiv="X-UA-Compatible"/>
|
||||
<style type="text/css">
|
||||
/* FONTS */
|
||||
@import url('https://fonts.googleapis.com/css?family=Nunito+Sans');
|
||||
|
||||
/* RESET STYLES */
|
||||
img { border: 0; height: auto; line-height: 100%; outline: none; text-decoration: none; }
|
||||
table { border-collapse: collapse !important; }
|
||||
body { height: 100% !important; margin: 0 !important; padding: 0 !important; width: 100% !important; }
|
||||
|
||||
/* iOS BLUE LINKS */
|
||||
a[x-apple-data-detectors] {
|
||||
color: inherit !important;
|
||||
text-decoration: none !important;
|
||||
font-size: inherit !important;
|
||||
font-family: inherit !important;
|
||||
font-weight: inherit !important;
|
||||
line-height: inherit !important;
|
||||
}
|
||||
|
||||
/* MOBILE STYLES */
|
||||
@media screen and (max-width:600px){
|
||||
h1 {
|
||||
font-size: 32px !important;
|
||||
line-height: 32px !important;
|
||||
/* CLIENT-SPECIFIC STYLES */
|
||||
body, table, td, a {
|
||||
-webkit-text-size-adjust: 100%;
|
||||
-ms-text-size-adjust: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
/* ANDROID CENTER FIX */
|
||||
div[style*="margin: 16px 0;"] { margin: 0 !important; }
|
||||
</style>
|
||||
table, td {
|
||||
mso-table-lspace: 0pt;
|
||||
mso-table-rspace: 0pt;
|
||||
}
|
||||
|
||||
img {
|
||||
-ms-interpolation-mode: bicubic;
|
||||
}
|
||||
|
||||
/* RESET STYLES */
|
||||
img {
|
||||
border: 0;
|
||||
height: auto;
|
||||
line-height: 100%;
|
||||
outline: none;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
table {
|
||||
border-collapse: collapse !important;
|
||||
}
|
||||
|
||||
body {
|
||||
height: 100% !important;
|
||||
margin: 0 !important;
|
||||
padding: 0 !important;
|
||||
width: 100% !important;
|
||||
}
|
||||
|
||||
/* iOS BLUE LINKS */
|
||||
a[x-apple-data-detectors] {
|
||||
color: inherit !important;
|
||||
text-decoration: none !important;
|
||||
font-size: inherit !important;
|
||||
font-family: inherit !important;
|
||||
font-weight: inherit !important;
|
||||
line-height: inherit !important;
|
||||
}
|
||||
|
||||
/* MOBILE STYLES */
|
||||
@media screen and (max-width: 600px) {
|
||||
h1 {
|
||||
font-size: 32px !important;
|
||||
line-height: 32px !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* ANDROID CENTER FIX */
|
||||
div[style*="margin: 16px 0;"] {
|
||||
margin: 0 !important;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body style="background-color: #f4f4f4; margin: 0 !important; padding: 0 !important;">
|
||||
|
||||
|
@ -54,17 +81,19 @@
|
|||
<table border="0" cellpadding="0" cellspacing="0" width="100%">
|
||||
<!-- LOGO -->
|
||||
<tr>
|
||||
<td bgcolor="#530EEC" align="center">
|
||||
<td align="center" bgcolor="#530EEC">
|
||||
<!--[if (gte mso 9)|(IE)]>
|
||||
<table align="center" border="0" cellspacing="0" cellpadding="0" width="600">
|
||||
<tr>
|
||||
<td align="center" valign="top" width="600">
|
||||
<![endif]-->
|
||||
<table border="0" cellpadding="0" cellspacing="0" width="100%" style="max-width: 600px;" >
|
||||
<tr>
|
||||
<td align="center" valign="top" style="padding: 40px 10px 40px 10px;">
|
||||
<td align="center" valign="top" width="600">
|
||||
<![endif]-->
|
||||
<table border="0" cellpadding="0" cellspacing="0" style="max-width: 600px;" width="100%">
|
||||
<tr>
|
||||
<td align="center" style="padding: 40px 10px 40px 10px;" valign="top">
|
||||
<a href="{{ args.home_url }}" target="_blank">
|
||||
<img alt="Logo" src="https://i.imgur.com/t0DPkyl.png" width="120" height="44" style="display: block; width: 150px; max-width: 150px; min-width: 150px; font-family: 'Nunito Sans', Helvetica, Arial, sans-serif; color: #ffffff; font-size: 18px;" border="0">
|
||||
<img alt="Logo" border="0" height="44" src="https://i.imgur.com/t0DPkyl.png"
|
||||
style="display: block; width: 150px; max-width: 150px; min-width: 150px; font-family: 'Nunito Sans', Helvetica, Arial, sans-serif; color: #ffffff; font-size: 18px;"
|
||||
width="120">
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
|
@ -78,17 +107,18 @@
|
|||
</tr>
|
||||
<!-- TITLE -->
|
||||
<tr>
|
||||
<td bgcolor="#530EEC" align="center" style="padding: 0px 10px 0px 10px;">
|
||||
<td align="center" bgcolor="#530EEC" style="padding: 0px 10px 0px 10px;">
|
||||
<!--[if (gte mso 9)|(IE)]>
|
||||
<table align="center" border="0" cellspacing="0" cellpadding="0" width="600">
|
||||
<tr>
|
||||
<td align="center" valign="top" width="600">
|
||||
<![endif]-->
|
||||
<table border="0" cellpadding="0" cellspacing="0" width="100%" style="max-width: 600px;" >
|
||||
<tr>
|
||||
<td bgcolor="#ffffff" align="center" valign="top" style="padding: 40px 20px 20px 20px; border-radius: 4px 4px 0px 0px; color: #221F1F; font-family: 'Nunito Sans', Helvetica, Arial, sans-serif; font-size: 48px; font-weight: 400; letter-spacing: 2px; line-height: 48px;">
|
||||
<h1 style="font-size: 42px; font-weight: 400; margin: 0;">
|
||||
{{ args.title }}
|
||||
<td align="center" valign="top" width="600">
|
||||
<![endif]-->
|
||||
<table border="0" cellpadding="0" cellspacing="0" style="max-width: 600px;" width="100%">
|
||||
<tr>
|
||||
<td align="center" bgcolor="#ffffff" style="padding: 40px 20px 20px 20px; border-radius: 4px 4px 0px 0px; color: #221F1F; font-family: 'Nunito Sans', Helvetica, Arial, sans-serif; font-size: 48px; font-weight: 400; letter-spacing: 2px; line-height: 48px;"
|
||||
valign="top">
|
||||
<h1 style="font-size: 42px; font-weight: 400; margin: 0;">
|
||||
{{ args.title }}
|
||||
</h1>
|
||||
</td>
|
||||
</tr>
|
||||
|
@ -102,18 +132,19 @@
|
|||
</tr>
|
||||
<!-- BODY -->
|
||||
<tr>
|
||||
<td bgcolor="#f4f4f4" align="center" style="padding: 0px 10px 0px 10px;">
|
||||
<td align="center" bgcolor="#f4f4f4" style="padding: 0px 10px 0px 10px;">
|
||||
<!--[if (gte mso 9)|(IE)]>
|
||||
<table align="center" border="0" cellspacing="0" cellpadding="0" width="600">
|
||||
<tr>
|
||||
<td align="center" valign="top" width="600">
|
||||
<tr>
|
||||
<td align="center" valign="top" width="600">
|
||||
<![endif]-->
|
||||
<table border="0" cellpadding="0" cellspacing="0" width="100%" style="max-width: 600px;" >
|
||||
<tr>
|
||||
<td bgcolor="#ffffff" style="padding: 20px 30px 40px 30px; color: #666666; font-family: 'Nunito Sans', Helvetica, Arial, sans-serif; font-size: 18px; font-weight: 400; line-height: 25px; border-radius: 0px 0px 4px 4px;" >
|
||||
{{ args.body }}
|
||||
</td>
|
||||
</tr>
|
||||
<table border="0" cellpadding="0" cellspacing="0" style="max-width: 600px;" width="100%">
|
||||
<tr>
|
||||
<td bgcolor="#ffffff"
|
||||
style="padding: 20px 30px 40px 30px; color: #666666; font-family: 'Nunito Sans', Helvetica, Arial, sans-serif; font-size: 18px; font-weight: 400; line-height: 25px; border-radius: 0px 0px 4px 4px;">
|
||||
{{ args.body }}
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<!--[if (gte mso 9)|(IE)]>
|
||||
</td>
|
||||
|
@ -124,43 +155,50 @@
|
|||
</tr>
|
||||
<!-- FOOTER -->
|
||||
<tr>
|
||||
<td bgcolor="#f4f4f4" align="center" style="padding: 0px 10px 0px 10px;">
|
||||
<td align="center" bgcolor="#f4f4f4" style="padding: 0px 10px 0px 10px;">
|
||||
<!--[if (gte mso 9)|(IE)]>
|
||||
<table align="center" border="0" cellspacing="0" cellpadding="0" width="600">
|
||||
<tr>
|
||||
<td align="center" valign="top" width="600">
|
||||
<tr>
|
||||
<td align="center" valign="top" width="600">
|
||||
<![endif]-->
|
||||
<table border="0" cellpadding="0" cellspacing="0" width="100%" style="max-width: 600px;" >
|
||||
<!-- NAVIGATION -->
|
||||
<tr>
|
||||
<td bgcolor="#f4f4f4" align="center" style="padding: 30px 30px 30px 30px; color: #666666; font-family: 'Nunito Sans', Helvetica, Arial, sans-serif; font-size: 14px; font-weight: 400; line-height: 18px;" >
|
||||
<p style="margin: 0;">
|
||||
<a href="{{ args.home_url }}" target="_blank" style="color: #221F1F; font-weight: 700;">Grant.io</a> -
|
||||
<a href="{{ args.account_url }}" target="_blank" style="color: #221F1F; font-weight: 700;">Your Account</a> -
|
||||
<a href="{{ args.email_settings_url }}" target="_blank" style="color: #221F1F; font-weight: 700;">Email Settings</a>
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
<!-- UNSUBSCRIBE -->
|
||||
<tr>
|
||||
<td bgcolor="#f4f4f4" align="center" style="padding: 0px 30px 30px 30px; color: #666666; font-family: 'Nunito Sans', Helvetica, Arial, sans-serif; font-size: 14px; font-weight: 400; line-height: 18px;" >
|
||||
<p style="margin: 0;">
|
||||
Don’t want anymore emails?
|
||||
<a href="{{ args.unsubscribe_url }}" target="_blank" style="color: #221F1F; font-weight: 700;">
|
||||
Click here to unsubscribe
|
||||
</a>
|
||||
.
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
<!-- ADDRESS -->
|
||||
<tr>
|
||||
<td bgcolor="#f4f4f4" align="center" style="padding: 0px 30px 30px 30px; color: #AAAAAA; font-family: 'Nunito Sans', Helvetica, Arial, sans-serif; font-size: 12px; font-weight: 400; line-height: 18px;" >
|
||||
<p style="margin: 0;">
|
||||
Grant.io Inc, 123 Address Street, Somewhere, NY 11211
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
<table border="0" cellpadding="0" cellspacing="0" style="max-width: 600px;" width="100%">
|
||||
<!-- NAVIGATION -->
|
||||
<tr>
|
||||
<td align="center" bgcolor="#f4f4f4"
|
||||
style="padding: 30px 30px 30px 30px; color: #666666; font-family: 'Nunito Sans', Helvetica, Arial, sans-serif; font-size: 14px; font-weight: 400; line-height: 18px;">
|
||||
<p style="margin: 0;">
|
||||
<a href="{{ args.home_url }}" style="color: #221F1F; font-weight: 700;" target="_blank">Grant.io</a>
|
||||
-
|
||||
<a href="{{ args.account_url }}" style="color: #221F1F; font-weight: 700;" target="_blank">Your
|
||||
Account</a> -
|
||||
<a href="{{ args.email_settings_url }}" style="color: #221F1F; font-weight: 700;"
|
||||
target="_blank">Email Settings</a>
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
<!-- UNSUBSCRIBE -->
|
||||
<tr>
|
||||
<td align="center" bgcolor="#f4f4f4"
|
||||
style="padding: 0px 30px 30px 30px; color: #666666; font-family: 'Nunito Sans', Helvetica, Arial, sans-serif; font-size: 14px; font-weight: 400; line-height: 18px;">
|
||||
<p style="margin: 0;">
|
||||
Don’t want anymore emails?
|
||||
<a href="{{ args.unsubscribe_url }}" style="color: #221F1F; font-weight: 700;"
|
||||
target="_blank">
|
||||
Click here to unsubscribe
|
||||
</a>
|
||||
.
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
<!-- ADDRESS -->
|
||||
<tr>
|
||||
<td align="center" bgcolor="#f4f4f4"
|
||||
style="padding: 0px 30px 30px 30px; color: #AAAAAA; font-family: 'Nunito Sans', Helvetica, Arial, sans-serif; font-size: 12px; font-weight: 400; line-height: 18px;">
|
||||
<p style="margin: 0;">
|
||||
Grant.io Inc, 123 Address Street, Somewhere, NY 11211
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<!--[if (gte mso 9)|(IE)]>
|
||||
</td>
|
||||
|
@ -170,6 +208,6 @@
|
|||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
from . import commands
|
||||
from . import views
|
||||
from . import models
|
||||
from . import views
|
||||
|
|
|
@ -11,15 +11,15 @@ def delete_user(identity):
|
|||
print(identity)
|
||||
user = None
|
||||
if str.isdigit(identity):
|
||||
user = User.get_by_id(identity)
|
||||
user = User.get_by_id(identity)
|
||||
else:
|
||||
user = User.get_by_email(identity)
|
||||
user = User.get_by_email(identity)
|
||||
|
||||
if user:
|
||||
db.session.delete(user)
|
||||
db.session.commit()
|
||||
click.echo(f'Succesfully deleted {user.display_name} (uid {user.id})')
|
||||
db.session.delete(user)
|
||||
db.session.commit()
|
||||
click.echo(f'Succesfully deleted {user.display_name} (uid {user.id})')
|
||||
else:
|
||||
raise click.BadParameter('Invalid user identity. Must be a userid, '\
|
||||
'account address, or email address of an '\
|
||||
'existing user.')
|
||||
raise click.BadParameter('Invalid user identity. Must be a userid, ' \
|
||||
'account address, or email address of an ' \
|
||||
'existing user.')
|
||||
|
|
|
@ -1,26 +1,24 @@
|
|||
from flask_security import UserMixin, RoleMixin
|
||||
from flask_security.core import current_user
|
||||
from flask_security.utils import hash_password, verify_and_update_password, login_user, logout_user
|
||||
from sqlalchemy.ext.hybrid import hybrid_property
|
||||
|
||||
from grant.utils.exceptions import ValidationException
|
||||
from grant.comment.models import Comment
|
||||
from grant.email.models import EmailVerification, EmailRecovery
|
||||
from grant.email.send import send_email
|
||||
from grant.extensions import ma, db, security
|
||||
from grant.utils.misc import make_url
|
||||
from grant.utils.social import generate_social_url
|
||||
from grant.utils.upload import extract_avatar_filename, construct_avatar_url
|
||||
from grant.email.subscription_settings import (
|
||||
get_default_email_subscriptions,
|
||||
email_subscriptions_to_bits,
|
||||
email_subscriptions_to_dict
|
||||
)
|
||||
from grant.extensions import ma, db, security
|
||||
from grant.utils.misc import make_url
|
||||
from grant.utils.social import generate_social_url
|
||||
from grant.utils.upload import extract_avatar_filename, construct_avatar_url
|
||||
from sqlalchemy.ext.hybrid import hybrid_property
|
||||
|
||||
|
||||
def is_current_authed_user_id(user_id):
|
||||
return current_user.is_authenticated and \
|
||||
current_user.id == user_id
|
||||
current_user.id == user_id
|
||||
|
||||
|
||||
class RolesUsers(db.Model):
|
||||
|
@ -233,6 +231,7 @@ class SocialMediaSchema(ma.Schema):
|
|||
"service",
|
||||
"username",
|
||||
)
|
||||
|
||||
url = ma.Method("get_url")
|
||||
|
||||
def get_url(self, obj):
|
||||
|
|
|
@ -1,11 +1,10 @@
|
|||
from flask import Blueprint, g, request
|
||||
from flask_yoloapi import endpoint, parameter
|
||||
from animal_case import keys_to_snake_case
|
||||
|
||||
from flask import Blueprint, g
|
||||
from flask_yoloapi import endpoint, parameter
|
||||
from grant.comment.models import Comment, user_comments_schema
|
||||
from grant.email.models import EmailRecovery
|
||||
from grant.proposal.models import (
|
||||
Proposal,
|
||||
proposals_schema,
|
||||
proposal_team,
|
||||
ProposalTeamInvite,
|
||||
invites_with_proposal_schema,
|
||||
|
@ -17,15 +16,13 @@ from grant.proposal.models import (
|
|||
REJECTED,
|
||||
CONFIRMED
|
||||
)
|
||||
from grant.utils.exceptions import ValidationException
|
||||
from grant.utils.auth import requires_auth, requires_same_user_auth, get_authed_user
|
||||
from grant.utils.upload import remove_avatar, sign_avatar_upload, AvatarException
|
||||
from grant.utils.exceptions import ValidationException
|
||||
from grant.utils.social import verify_social, get_social_login_url, VerifySocialException
|
||||
from grant.email.models import EmailRecovery
|
||||
from grant.utils.upload import remove_avatar, sign_avatar_upload, AvatarException
|
||||
|
||||
from .models import (
|
||||
User,
|
||||
UserSettings,
|
||||
SocialMedia,
|
||||
Avatar,
|
||||
users_schema,
|
||||
|
@ -48,10 +45,10 @@ def get_users(proposal_id):
|
|||
else:
|
||||
users = (
|
||||
User.query
|
||||
.join(proposal_team)
|
||||
.join(Proposal)
|
||||
.filter(proposal_team.c.proposal_id == proposal.id)
|
||||
.all()
|
||||
.join(proposal_team)
|
||||
.join(Proposal)
|
||||
.filter(proposal_team.c.proposal_id == proposal.id)
|
||||
.all()
|
||||
)
|
||||
result = users_schema.dump(users)
|
||||
return result
|
||||
|
|
|
@ -1,10 +1,9 @@
|
|||
from functools import wraps
|
||||
from hashlib import sha256
|
||||
|
||||
from flask import session
|
||||
|
||||
from grant.settings import SECRET_KEY, ADMIN_PASS_HASH
|
||||
|
||||
|
||||
admin_auth = {
|
||||
"username": "admin",
|
||||
"password": ADMIN_PASS_HASH,
|
||||
|
|
|
@ -1,14 +1,10 @@
|
|||
import ast
|
||||
import json
|
||||
from functools import wraps
|
||||
|
||||
import requests
|
||||
from flask_security.core import current_user
|
||||
from flask import request, g, jsonify
|
||||
import sentry_sdk
|
||||
|
||||
from grant.settings import SECRET_KEY, BLOCKCHAIN_API_SECRET
|
||||
from flask import request, g, jsonify
|
||||
from flask_security.core import current_user
|
||||
from grant.proposal.models import Proposal
|
||||
from grant.settings import BLOCKCHAIN_API_SECRET
|
||||
from grant.user.models import User
|
||||
|
||||
|
||||
|
@ -27,6 +23,7 @@ def requires_auth(f):
|
|||
"id": current_user.id,
|
||||
}
|
||||
return f(*args, **kwargs)
|
||||
|
||||
return decorated
|
||||
|
||||
|
||||
|
@ -68,6 +65,7 @@ def requires_team_member_auth(f):
|
|||
|
||||
return requires_auth(decorated)
|
||||
|
||||
|
||||
def internal_webhook(f):
|
||||
@wraps(f)
|
||||
def decorated(*args, **kwargs):
|
||||
|
@ -79,4 +77,5 @@ def internal_webhook(f):
|
|||
print(f'Internal webhook provided invalid "Authorization" header: {secret}')
|
||||
return jsonify(message="Invalid 'Authorization' header"), 403
|
||||
return f(*args, **kwargs)
|
||||
|
||||
return decorated
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import math
|
||||
|
||||
|
||||
def get_quarter_formatted(date):
|
||||
return "Q" + str(math.ceil(date.date_created.month / 3.)) + " " + str(date.date_created.year)
|
||||
return "Q" + str(math.ceil(date.date_created.month / 3.)) + " " + str(date.date_created.year)
|
||||
|
|
|
@ -1,2 +1,2 @@
|
|||
class ValidationException(Exception):
|
||||
pass
|
||||
pass
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
import datetime
|
||||
import time
|
||||
import random
|
||||
import string
|
||||
import re
|
||||
import string
|
||||
import time
|
||||
|
||||
from grant.settings import SITE_URL
|
||||
|
||||
epoch = datetime.datetime.utcfromtimestamp(0)
|
||||
|
@ -20,35 +21,41 @@ def dt_to_ms(dt):
|
|||
def dt_to_unix(dt):
|
||||
return int(time.mktime(dt.timetuple()))
|
||||
|
||||
|
||||
def gen_random_code(length=32):
|
||||
return ''.join(
|
||||
[random.choice(string.ascii_letters + string.digits) for n in range(length)]
|
||||
)
|
||||
return ''.join(
|
||||
[random.choice(string.ascii_letters + string.digits) for n in range(length)]
|
||||
)
|
||||
|
||||
|
||||
def make_url(path: str):
|
||||
return f'{SITE_URL}{path}'
|
||||
return f'{SITE_URL}{path}'
|
||||
|
||||
|
||||
def is_email(email: str):
|
||||
return bool(re.match(r"[^@]+@[^@]+\.[^@]+", email))
|
||||
return bool(re.match(r"[^@]+@[^@]+\.[^@]+", email))
|
||||
|
||||
|
||||
def from_zat(zat: int):
|
||||
return zat / 100000000
|
||||
return zat / 100000000
|
||||
|
||||
|
||||
def to_zat(zec: float):
|
||||
return zec * 100000000
|
||||
return zec * 100000000
|
||||
|
||||
|
||||
def make_preview(content: str, max_length: int):
|
||||
truncated = False
|
||||
truncated = False
|
||||
|
||||
# Show only the first line. Add ellipsis if there are more than two lines,
|
||||
# even if first line isn't truncated.
|
||||
preview = content.split('\n', 1)[0]
|
||||
if len(preview) != len(content):
|
||||
truncated = True
|
||||
# Show only the first line. Add ellipsis if there are more than two lines,
|
||||
# even if first line isn't truncated.
|
||||
preview = content.split('\n', 1)[0]
|
||||
if len(preview) != len(content):
|
||||
truncated = True
|
||||
|
||||
# Truncate to max length
|
||||
if len(preview) > max_length:
|
||||
preview = preview[:max_length - 3]
|
||||
truncated = True
|
||||
# Truncate to max length
|
||||
if len(preview) > max_length:
|
||||
preview = preview[:max_length - 3]
|
||||
truncated = True
|
||||
|
||||
return content + '...' if truncated else content
|
||||
return content + '...' if truncated else content
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import requests
|
||||
import json
|
||||
from grant.settings import BLOCKCHAIN_REST_API_URL, BLOCKCHAIN_API_SECRET
|
||||
|
||||
|
||||
### REST API ###
|
||||
|
||||
def handle_res(res):
|
||||
|
@ -10,18 +10,20 @@ def handle_res(res):
|
|||
raise Exception('Blockchain API Error: {}'.format(j['error']))
|
||||
return j['data']
|
||||
|
||||
def blockchain_get(path, params = None):
|
||||
|
||||
def blockchain_get(path, params=None):
|
||||
res = requests.get(
|
||||
f'{BLOCKCHAIN_REST_API_URL}{path}',
|
||||
headers={ 'authorization': BLOCKCHAIN_API_SECRET },
|
||||
headers={'authorization': BLOCKCHAIN_API_SECRET},
|
||||
params=params,
|
||||
)
|
||||
return handle_res(res)
|
||||
|
||||
def blockchain_post(path, data = None):
|
||||
|
||||
def blockchain_post(path, data=None):
|
||||
res = requests.post(
|
||||
f'{BLOCKCHAIN_REST_API_URL}{path}',
|
||||
headers={ 'authorization': BLOCKCHAIN_API_SECRET },
|
||||
headers={'authorization': BLOCKCHAIN_API_SECRET},
|
||||
json=data,
|
||||
)
|
||||
return handle_res(res)
|
||||
|
|
|
@ -1,10 +1,5 @@
|
|||
import requests
|
||||
import re
|
||||
import json
|
||||
import pprint
|
||||
import abc
|
||||
from requests_oauthlib import OAuth1Session, OAuth2Session
|
||||
from flask import session
|
||||
|
||||
from grant.settings import (
|
||||
SITE_URL,
|
||||
GITHUB_CLIENT_ID,
|
||||
|
@ -14,6 +9,7 @@ from grant.settings import (
|
|||
LINKEDIN_CLIENT_ID,
|
||||
LINKEDIN_CLIENT_SECRET
|
||||
)
|
||||
from requests_oauthlib import OAuth1Session, OAuth2Session
|
||||
|
||||
|
||||
class VerifySocialException(Exception):
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import re
|
||||
import uuid
|
||||
|
||||
import boto3
|
||||
from flask import current_app
|
||||
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
from __future__ import with_statement
|
||||
|
||||
import logging
|
||||
from logging.config import fileConfig
|
||||
|
||||
from alembic import context
|
||||
from sqlalchemy import engine_from_config, pool
|
||||
from logging.config import fileConfig
|
||||
import logging
|
||||
|
||||
# this is the Alembic Config object, which provides
|
||||
# access to the values within the .ini file in use.
|
||||
|
@ -18,10 +20,12 @@ logger = logging.getLogger('alembic.env')
|
|||
# from myapp import mymodel
|
||||
# target_metadata = mymodel.Base.metadata
|
||||
from flask import current_app
|
||||
|
||||
config.set_main_option('sqlalchemy.url',
|
||||
current_app.config.get('SQLALCHEMY_DATABASE_URI'))
|
||||
target_metadata = current_app.extensions['migrate'].db.metadata
|
||||
|
||||
|
||||
# other values from the config, defined by the needs of env.py,
|
||||
# can be acquired:
|
||||
# my_important_option = config.get_main_option("my_important_option")
|
||||
|
@ -81,6 +85,7 @@ def run_migrations_online():
|
|||
finally:
|
||||
connection.close()
|
||||
|
||||
|
||||
if context.is_offline_mode():
|
||||
run_migrations_offline()
|
||||
else:
|
||||
|
|
|
@ -17,8 +17,8 @@ depends_on = ${repr(depends_on)}
|
|||
|
||||
|
||||
def upgrade():
|
||||
${upgrades if upgrades else "pass"}
|
||||
${upgrades if upgrades else "pass"}
|
||||
|
||||
|
||||
def downgrade():
|
||||
${downgrades if downgrades else "pass"}
|
||||
${downgrades if downgrades else "pass"}
|
||||
|
|
|
@ -5,9 +5,8 @@ Revises:
|
|||
Create Date: 2019-01-09 16:35:34.349666
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
from alembic import op
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '4af29f8b2143'
|
||||
|
@ -19,139 +18,139 @@ depends_on = None
|
|||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.create_table('proposal',
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
sa.Column('date_created', sa.DateTime(), nullable=True),
|
||||
sa.Column('status', sa.String(length=255), nullable=False),
|
||||
sa.Column('title', sa.String(length=255), nullable=False),
|
||||
sa.Column('brief', sa.String(length=255), nullable=False),
|
||||
sa.Column('stage', sa.String(length=255), nullable=False),
|
||||
sa.Column('content', sa.Text(), nullable=False),
|
||||
sa.Column('category', sa.String(length=255), nullable=False),
|
||||
sa.Column('date_approved', sa.DateTime(), nullable=True),
|
||||
sa.Column('date_published', sa.DateTime(), nullable=True),
|
||||
sa.Column('reject_reason', sa.String(length=255), nullable=True),
|
||||
sa.Column('target', sa.String(length=255), nullable=False),
|
||||
sa.Column('payout_address', sa.String(length=255), nullable=False),
|
||||
sa.Column('deadline_duration', sa.Integer(), nullable=False),
|
||||
sa.PrimaryKeyConstraint('id')
|
||||
)
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
sa.Column('date_created', sa.DateTime(), nullable=True),
|
||||
sa.Column('status', sa.String(length=255), nullable=False),
|
||||
sa.Column('title', sa.String(length=255), nullable=False),
|
||||
sa.Column('brief', sa.String(length=255), nullable=False),
|
||||
sa.Column('stage', sa.String(length=255), nullable=False),
|
||||
sa.Column('content', sa.Text(), nullable=False),
|
||||
sa.Column('category', sa.String(length=255), nullable=False),
|
||||
sa.Column('date_approved', sa.DateTime(), nullable=True),
|
||||
sa.Column('date_published', sa.DateTime(), nullable=True),
|
||||
sa.Column('reject_reason', sa.String(length=255), nullable=True),
|
||||
sa.Column('target', sa.String(length=255), nullable=False),
|
||||
sa.Column('payout_address', sa.String(length=255), nullable=False),
|
||||
sa.Column('deadline_duration', sa.Integer(), nullable=False),
|
||||
sa.PrimaryKeyConstraint('id')
|
||||
)
|
||||
op.create_table('role',
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
sa.Column('name', sa.String(length=80), nullable=True),
|
||||
sa.Column('description', sa.String(length=255), nullable=True),
|
||||
sa.PrimaryKeyConstraint('id'),
|
||||
sa.UniqueConstraint('name')
|
||||
)
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
sa.Column('name', sa.String(length=80), nullable=True),
|
||||
sa.Column('description', sa.String(length=255), nullable=True),
|
||||
sa.PrimaryKeyConstraint('id'),
|
||||
sa.UniqueConstraint('name')
|
||||
)
|
||||
op.create_table('user',
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
sa.Column('email_address', sa.String(length=255), nullable=False),
|
||||
sa.Column('password', sa.String(length=255), nullable=False),
|
||||
sa.Column('display_name', sa.String(length=255), nullable=True),
|
||||
sa.Column('title', sa.String(length=255), nullable=True),
|
||||
sa.Column('active', sa.Boolean(), nullable=True),
|
||||
sa.PrimaryKeyConstraint('id'),
|
||||
sa.UniqueConstraint('email_address')
|
||||
)
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
sa.Column('email_address', sa.String(length=255), nullable=False),
|
||||
sa.Column('password', sa.String(length=255), nullable=False),
|
||||
sa.Column('display_name', sa.String(length=255), nullable=True),
|
||||
sa.Column('title', sa.String(length=255), nullable=True),
|
||||
sa.Column('active', sa.Boolean(), nullable=True),
|
||||
sa.PrimaryKeyConstraint('id'),
|
||||
sa.UniqueConstraint('email_address')
|
||||
)
|
||||
op.create_table('avatar',
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
sa.Column('image_url', sa.String(length=255), nullable=True),
|
||||
sa.Column('user_id', sa.Integer(), nullable=True),
|
||||
sa.ForeignKeyConstraint(['user_id'], ['user.id'], ),
|
||||
sa.PrimaryKeyConstraint('id')
|
||||
)
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
sa.Column('image_url', sa.String(length=255), nullable=True),
|
||||
sa.Column('user_id', sa.Integer(), nullable=True),
|
||||
sa.ForeignKeyConstraint(['user_id'], ['user.id'], ),
|
||||
sa.PrimaryKeyConstraint('id')
|
||||
)
|
||||
op.create_table('comment',
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
sa.Column('date_created', sa.DateTime(), nullable=True),
|
||||
sa.Column('content', sa.Text(), nullable=False),
|
||||
sa.Column('parent_comment_id', sa.Integer(), nullable=True),
|
||||
sa.Column('proposal_id', sa.Integer(), nullable=False),
|
||||
sa.Column('user_id', sa.Integer(), nullable=False),
|
||||
sa.ForeignKeyConstraint(['parent_comment_id'], ['comment.id'], ),
|
||||
sa.ForeignKeyConstraint(['proposal_id'], ['proposal.id'], ),
|
||||
sa.ForeignKeyConstraint(['user_id'], ['user.id'], ),
|
||||
sa.PrimaryKeyConstraint('id')
|
||||
)
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
sa.Column('date_created', sa.DateTime(), nullable=True),
|
||||
sa.Column('content', sa.Text(), nullable=False),
|
||||
sa.Column('parent_comment_id', sa.Integer(), nullable=True),
|
||||
sa.Column('proposal_id', sa.Integer(), nullable=False),
|
||||
sa.Column('user_id', sa.Integer(), nullable=False),
|
||||
sa.ForeignKeyConstraint(['parent_comment_id'], ['comment.id'], ),
|
||||
sa.ForeignKeyConstraint(['proposal_id'], ['proposal.id'], ),
|
||||
sa.ForeignKeyConstraint(['user_id'], ['user.id'], ),
|
||||
sa.PrimaryKeyConstraint('id')
|
||||
)
|
||||
op.create_table('email_recovery',
|
||||
sa.Column('user_id', sa.Integer(), nullable=False),
|
||||
sa.Column('code', sa.String(length=255), nullable=False),
|
||||
sa.Column('date_created', sa.DateTime(), nullable=True),
|
||||
sa.ForeignKeyConstraint(['user_id'], ['user.id'], ),
|
||||
sa.PrimaryKeyConstraint('user_id'),
|
||||
sa.UniqueConstraint('code')
|
||||
)
|
||||
sa.Column('user_id', sa.Integer(), nullable=False),
|
||||
sa.Column('code', sa.String(length=255), nullable=False),
|
||||
sa.Column('date_created', sa.DateTime(), nullable=True),
|
||||
sa.ForeignKeyConstraint(['user_id'], ['user.id'], ),
|
||||
sa.PrimaryKeyConstraint('user_id'),
|
||||
sa.UniqueConstraint('code')
|
||||
)
|
||||
op.create_table('email_verification',
|
||||
sa.Column('user_id', sa.Integer(), nullable=False),
|
||||
sa.Column('code', sa.String(length=255), nullable=False),
|
||||
sa.Column('has_verified', sa.Boolean(), nullable=True),
|
||||
sa.ForeignKeyConstraint(['user_id'], ['user.id'], ),
|
||||
sa.PrimaryKeyConstraint('user_id'),
|
||||
sa.UniqueConstraint('code')
|
||||
)
|
||||
sa.Column('user_id', sa.Integer(), nullable=False),
|
||||
sa.Column('code', sa.String(length=255), nullable=False),
|
||||
sa.Column('has_verified', sa.Boolean(), nullable=True),
|
||||
sa.ForeignKeyConstraint(['user_id'], ['user.id'], ),
|
||||
sa.PrimaryKeyConstraint('user_id'),
|
||||
sa.UniqueConstraint('code')
|
||||
)
|
||||
op.create_table('milestone',
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
sa.Column('date_created', sa.DateTime(), nullable=False),
|
||||
sa.Column('title', sa.String(length=255), nullable=False),
|
||||
sa.Column('content', sa.Text(), nullable=False),
|
||||
sa.Column('stage', sa.String(length=255), nullable=False),
|
||||
sa.Column('payout_percent', sa.String(length=255), nullable=False),
|
||||
sa.Column('immediate_payout', sa.Boolean(), nullable=True),
|
||||
sa.Column('date_estimated', sa.DateTime(), nullable=False),
|
||||
sa.Column('proposal_id', sa.Integer(), nullable=False),
|
||||
sa.ForeignKeyConstraint(['proposal_id'], ['proposal.id'], ),
|
||||
sa.PrimaryKeyConstraint('id')
|
||||
)
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
sa.Column('date_created', sa.DateTime(), nullable=False),
|
||||
sa.Column('title', sa.String(length=255), nullable=False),
|
||||
sa.Column('content', sa.Text(), nullable=False),
|
||||
sa.Column('stage', sa.String(length=255), nullable=False),
|
||||
sa.Column('payout_percent', sa.String(length=255), nullable=False),
|
||||
sa.Column('immediate_payout', sa.Boolean(), nullable=True),
|
||||
sa.Column('date_estimated', sa.DateTime(), nullable=False),
|
||||
sa.Column('proposal_id', sa.Integer(), nullable=False),
|
||||
sa.ForeignKeyConstraint(['proposal_id'], ['proposal.id'], ),
|
||||
sa.PrimaryKeyConstraint('id')
|
||||
)
|
||||
op.create_table('proposal_contribution',
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
sa.Column('date_created', sa.DateTime(), nullable=False),
|
||||
sa.Column('proposal_id', sa.Integer(), nullable=False),
|
||||
sa.Column('user_id', sa.Integer(), nullable=True),
|
||||
sa.Column('status', sa.String(length=255), nullable=False),
|
||||
sa.Column('amount', sa.String(length=255), nullable=False),
|
||||
sa.Column('tx_id', sa.String(length=255), nullable=True),
|
||||
sa.ForeignKeyConstraint(['proposal_id'], ['proposal.id'], ),
|
||||
sa.ForeignKeyConstraint(['user_id'], ['user.id'], ),
|
||||
sa.PrimaryKeyConstraint('id')
|
||||
)
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
sa.Column('date_created', sa.DateTime(), nullable=False),
|
||||
sa.Column('proposal_id', sa.Integer(), nullable=False),
|
||||
sa.Column('user_id', sa.Integer(), nullable=True),
|
||||
sa.Column('status', sa.String(length=255), nullable=False),
|
||||
sa.Column('amount', sa.String(length=255), nullable=False),
|
||||
sa.Column('tx_id', sa.String(length=255), nullable=True),
|
||||
sa.ForeignKeyConstraint(['proposal_id'], ['proposal.id'], ),
|
||||
sa.ForeignKeyConstraint(['user_id'], ['user.id'], ),
|
||||
sa.PrimaryKeyConstraint('id')
|
||||
)
|
||||
op.create_table('proposal_team',
|
||||
sa.Column('user_id', sa.Integer(), nullable=True),
|
||||
sa.Column('proposal_id', sa.Integer(), nullable=True),
|
||||
sa.ForeignKeyConstraint(['proposal_id'], ['proposal.id'], ),
|
||||
sa.ForeignKeyConstraint(['user_id'], ['user.id'], )
|
||||
)
|
||||
sa.Column('user_id', sa.Integer(), nullable=True),
|
||||
sa.Column('proposal_id', sa.Integer(), nullable=True),
|
||||
sa.ForeignKeyConstraint(['proposal_id'], ['proposal.id'], ),
|
||||
sa.ForeignKeyConstraint(['user_id'], ['user.id'], )
|
||||
)
|
||||
op.create_table('proposal_team_invite',
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
sa.Column('date_created', sa.DateTime(), nullable=True),
|
||||
sa.Column('proposal_id', sa.Integer(), nullable=False),
|
||||
sa.Column('address', sa.String(length=255), nullable=False),
|
||||
sa.Column('accepted', sa.Boolean(), nullable=True),
|
||||
sa.ForeignKeyConstraint(['proposal_id'], ['proposal.id'], ),
|
||||
sa.PrimaryKeyConstraint('id')
|
||||
)
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
sa.Column('date_created', sa.DateTime(), nullable=True),
|
||||
sa.Column('proposal_id', sa.Integer(), nullable=False),
|
||||
sa.Column('address', sa.String(length=255), nullable=False),
|
||||
sa.Column('accepted', sa.Boolean(), nullable=True),
|
||||
sa.ForeignKeyConstraint(['proposal_id'], ['proposal.id'], ),
|
||||
sa.PrimaryKeyConstraint('id')
|
||||
)
|
||||
op.create_table('proposal_update',
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
sa.Column('date_created', sa.DateTime(), nullable=True),
|
||||
sa.Column('proposal_id', sa.Integer(), nullable=False),
|
||||
sa.Column('title', sa.String(length=255), nullable=False),
|
||||
sa.Column('content', sa.Text(), nullable=False),
|
||||
sa.ForeignKeyConstraint(['proposal_id'], ['proposal.id'], ),
|
||||
sa.PrimaryKeyConstraint('id')
|
||||
)
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
sa.Column('date_created', sa.DateTime(), nullable=True),
|
||||
sa.Column('proposal_id', sa.Integer(), nullable=False),
|
||||
sa.Column('title', sa.String(length=255), nullable=False),
|
||||
sa.Column('content', sa.Text(), nullable=False),
|
||||
sa.ForeignKeyConstraint(['proposal_id'], ['proposal.id'], ),
|
||||
sa.PrimaryKeyConstraint('id')
|
||||
)
|
||||
op.create_table('roles_users',
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
sa.Column('user_id', sa.Integer(), nullable=True),
|
||||
sa.Column('role_id', sa.Integer(), nullable=True),
|
||||
sa.ForeignKeyConstraint(['role_id'], ['role.id'], ),
|
||||
sa.ForeignKeyConstraint(['user_id'], ['user.id'], ),
|
||||
sa.PrimaryKeyConstraint('id')
|
||||
)
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
sa.Column('user_id', sa.Integer(), nullable=True),
|
||||
sa.Column('role_id', sa.Integer(), nullable=True),
|
||||
sa.ForeignKeyConstraint(['role_id'], ['role.id'], ),
|
||||
sa.ForeignKeyConstraint(['user_id'], ['user.id'], ),
|
||||
sa.PrimaryKeyConstraint('id')
|
||||
)
|
||||
op.create_table('social_media',
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
sa.Column('service', sa.String(length=255), nullable=False),
|
||||
sa.Column('username', sa.String(length=255), nullable=False),
|
||||
sa.Column('user_id', sa.Integer(), nullable=False),
|
||||
sa.ForeignKeyConstraint(['user_id'], ['user.id'], ),
|
||||
sa.PrimaryKeyConstraint('id')
|
||||
)
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
sa.Column('service', sa.String(length=255), nullable=False),
|
||||
sa.Column('username', sa.String(length=255), nullable=False),
|
||||
sa.Column('user_id', sa.Integer(), nullable=False),
|
||||
sa.ForeignKeyConstraint(['user_id'], ['user.id'], ),
|
||||
sa.PrimaryKeyConstraint('id')
|
||||
)
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
|
|
|
@ -5,9 +5,8 @@ Revises: 4af29f8b2143
|
|||
Create Date: 2019-01-10 14:44:42.536248
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
from alembic import op
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = 'e0d970ed6500'
|
||||
|
@ -19,12 +18,12 @@ depends_on = None
|
|||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.create_table('user_settings',
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
sa.Column('email_subscriptions', sa.Integer(), nullable=True),
|
||||
sa.Column('user_id', sa.Integer(), nullable=False),
|
||||
sa.ForeignKeyConstraint(['user_id'], ['user.id'], ),
|
||||
sa.PrimaryKeyConstraint('id')
|
||||
)
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
sa.Column('email_subscriptions', sa.Integer(), nullable=True),
|
||||
sa.Column('user_id', sa.Integer(), nullable=False),
|
||||
sa.ForeignKeyConstraint(['user_id'], ['user.id'], ),
|
||||
sa.PrimaryKeyConstraint('id')
|
||||
)
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
[flake8]
|
||||
ignore = D401
|
||||
max-line-length=120
|
||||
max-line-length = 120
|
||||
|
|
|
@ -1,10 +1,8 @@
|
|||
import json
|
||||
from grant.proposal.models import APPROVED, REJECTED
|
||||
from grant.utils.admin import generate_admin_password_hash
|
||||
from mock import patch
|
||||
|
||||
from grant.utils.admin import generate_admin_password_hash
|
||||
from grant.proposal.models import Proposal, APPROVED, REJECTED, PENDING, DRAFT
|
||||
from ..config import BaseProposalCreatorConfig
|
||||
from ..test_data import test_proposal, test_user
|
||||
|
||||
plaintext_mock_password = "p4ssw0rd"
|
||||
mock_admin_auth = {
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
import json
|
||||
from flask_testing import TestCase
|
||||
|
||||
from flask_testing import TestCase
|
||||
from grant.app import create_app
|
||||
from grant.user.models import User, SocialMedia, db, Avatar
|
||||
from grant.proposal.models import Proposal
|
||||
from .test_data import test_user, test_other_user, test_proposal, message
|
||||
from grant.user.models import User, SocialMedia, db, Avatar
|
||||
|
||||
from .test_data import test_user, test_other_user, test_proposal
|
||||
|
||||
|
||||
class BaseTestConfig(TestCase):
|
||||
|
@ -29,7 +30,7 @@ class BaseTestConfig(TestCase):
|
|||
"""
|
||||
|
||||
message = message or 'HTTP Status %s expected but got %s. Response json: %s' \
|
||||
% (status_code, response.status_code, response.json or response.data)
|
||||
% (status_code, response.status_code, response.json or response.data)
|
||||
self.assertEqual(response.status_code, status_code, message)
|
||||
|
||||
assert_status = assertStatus
|
||||
|
|
|
@ -1,16 +1,11 @@
|
|||
import copy
|
||||
import json
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
from animal_case import animalify
|
||||
from grant.proposal.models import Proposal
|
||||
from grant.user.models import User, user_schema, db
|
||||
from grant.email.subscription_settings import get_default_email_subscriptions
|
||||
from grant.email.models import EmailVerification
|
||||
from mock import patch, Mock
|
||||
from grant.user.models import db
|
||||
from mock import patch
|
||||
|
||||
from ..config import BaseUserConfig
|
||||
from ..test_data import test_team, test_proposal, test_user
|
||||
from ..test_data import test_user
|
||||
|
||||
|
||||
class TestEmailAPI(BaseUserConfig):
|
||||
|
|
|
@ -1,9 +1,4 @@
|
|||
import json
|
||||
from mock import patch
|
||||
import pytest
|
||||
|
||||
from ..config import BaseTestConfig
|
||||
from grant.utils.exceptions import ValidationException
|
||||
from grant.email.subscription_settings import (
|
||||
email_subscriptions_to_bits,
|
||||
email_subscriptions_to_dict,
|
||||
|
@ -12,6 +7,9 @@ from grant.email.subscription_settings import (
|
|||
is_subscribed,
|
||||
EmailSubscription
|
||||
)
|
||||
from grant.utils.exceptions import ValidationException
|
||||
|
||||
from ..config import BaseTestConfig
|
||||
|
||||
test_dict = get_default_email_subscriptions()
|
||||
|
||||
|
|
|
@ -26,4 +26,3 @@ class UserFactory(BaseFactory):
|
|||
|
||||
class Meta:
|
||||
"""Factory configuration."""
|
||||
|
||||
|
|
|
@ -2,6 +2,8 @@ def mock_request(response):
|
|||
def mock_request_func(*args, **kwargs):
|
||||
class MockResponse:
|
||||
def json(self):
|
||||
return { 'data': response }
|
||||
return {'data': response}
|
||||
|
||||
return MockResponse()
|
||||
|
||||
return mock_request_func
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
import json
|
||||
from mock import patch
|
||||
|
||||
from grant.proposal.models import Proposal, APPROVED, PENDING, DRAFT
|
||||
from grant.proposal.models import Proposal, PENDING
|
||||
|
||||
from ..config import BaseProposalCreatorConfig
|
||||
from ..test_data import test_proposal, test_user
|
||||
from ..test_data import test_proposal
|
||||
|
||||
|
||||
class TestProposalAPI(BaseProposalCreatorConfig):
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import json
|
||||
from mock import patch
|
||||
|
||||
from grant.proposal.models import Proposal, db
|
||||
|
||||
from ..config import BaseUserConfig
|
||||
from ..test_data import test_comment, test_reply
|
||||
|
||||
|
|
|
@ -2,10 +2,8 @@ import json
|
|||
from mock import patch
|
||||
|
||||
from grant.proposal.models import Proposal
|
||||
from grant.user.models import SocialMedia, Avatar
|
||||
from grant.utils.requests import blockchain_get
|
||||
from ..config import BaseUserConfig
|
||||
from ..test_data import test_proposal, test_user
|
||||
from ..test_data import test_proposal
|
||||
from ..mocks import mock_request
|
||||
|
||||
mock_contribution_addresses = mock_request({
|
||||
|
@ -14,6 +12,7 @@ mock_contribution_addresses = mock_request({
|
|||
'memo': '123',
|
||||
})
|
||||
|
||||
|
||||
class TestProposalContributionAPI(BaseUserConfig):
|
||||
@patch('requests.get', side_effect=mock_contribution_addresses)
|
||||
def test_create_proposal_contribution(self, mock_blockchain_get):
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
import json
|
||||
from mock import patch
|
||||
|
||||
from grant.proposal.models import Proposal, ProposalTeamInvite, db
|
||||
from grant.proposal.models import ProposalTeamInvite, db
|
||||
|
||||
from ..config import BaseProposalCreatorConfig
|
||||
from ..test_data import test_proposal, test_user
|
||||
|
||||
|
||||
class TestProposalInviteAPI(BaseProposalCreatorConfig):
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
import json
|
||||
from mock import patch
|
||||
|
||||
from grant.proposal.models import Proposal, db
|
||||
from ..config import BaseProposalCreatorConfig
|
||||
|
||||
test_update = {
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
import json
|
||||
import random
|
||||
|
||||
from grant.proposal.models import CATEGORIES
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
"""Sample test for CI"""
|
||||
|
||||
|
||||
def test_runs():
|
||||
assert True
|
||||
assert True
|
||||
|
|
|
@ -1,10 +1,7 @@
|
|||
import json
|
||||
from mock import patch
|
||||
|
||||
from grant.proposal.models import Proposal, ProposalTeamInvite, db
|
||||
from grant.user.models import SocialMedia, Avatar
|
||||
from grant.proposal.models import ProposalTeamInvite, db
|
||||
from ..config import BaseProposalCreatorConfig
|
||||
from ..test_data import test_proposal, test_user
|
||||
|
||||
|
||||
class TestUserInviteAPI(BaseProposalCreatorConfig):
|
||||
|
|
|
@ -3,13 +3,12 @@ import json
|
|||
from datetime import datetime, timedelta
|
||||
|
||||
from animal_case import animalify
|
||||
from grant.proposal.models import Proposal
|
||||
from grant.user.models import User, user_schema, db
|
||||
from grant.email.subscription_settings import get_default_email_subscriptions
|
||||
from mock import patch, Mock
|
||||
from grant.user.models import User, user_schema, db
|
||||
from mock import patch
|
||||
|
||||
from ..config import BaseUserConfig
|
||||
from ..test_data import test_team, test_proposal, test_user
|
||||
from ..test_data import test_user
|
||||
|
||||
|
||||
class TestUserAPI(BaseUserConfig):
|
||||
|
|
Loading…
Reference in New Issue