Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

NgoFormsAPI #154

Merged
merged 5 commits into from
Jan 31, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# Generated by Django 4.2.9 on 2024-01-31 12:37

from django.db import migrations, models
import donations.models.main
import functools


class Migration(migrations.Migration):

dependencies = [
("donations", "0005_alter_ngo_prefilled_form"),
]

operations = [
migrations.AlterField(
model_name="donor",
name="pdf_file",
field=models.FileField(
blank=True,
upload_to=functools.partial(
donations.models.main.year_ngo_donor_directory_path, *("donation-forms",), **{}
),
verbose_name="PDF file",
),
),
migrations.AlterField(
model_name="ngo",
name="prefilled_form",
field=models.FileField(
blank=True,
storage=donations.models.main.select_public_storage,
upload_to=functools.partial(donations.models.main.year_ngo_directory_path, *("ngo-forms",), **{}),
verbose_name="form with prefilled ngo data",
),
),
]
39 changes: 28 additions & 11 deletions backend/donations/models/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,24 +15,39 @@ def select_public_storage():
return storages["public"]


def _id_code(prefix: str, id: int) -> str:
def hash_id_secret(prefix: str, id: int) -> str:
return hashlib.sha1(f"{prefix}-{id}-{settings.SECRET_KEY}".encode()).hexdigest()[:10]


def ngo_directory_path(subdir, instance, filename) -> str:
# file will be uploaded to MEDIA_ROOT/ngo-<id>-<hash>/<subdir>/<filename>
return "{0}/ngo-{1}-{2}/{3}".format(subdir, instance.pk, _id_code("ngo", instance.pk), filename)
def ngo_directory_path(subdir: str, instance: "Ngo", filename: str) -> str:
"""
The file will be uploaded to MEDIA_ROOT/<subdir>/ngo-<id>-<hash>/<filename>
"""
return "{0}/ngo-{1}-{2}/{3}".format(subdir, instance.pk, hash_id_secret("ngo", instance.pk), filename)


def year_ngo_donor_directory_path(subdir, instance, filename) -> str:
def year_ngo_directory_path(subdir: str, instance: "Ngo", filename: str) -> str:
"""
The file will be uploaded to MEDIA_ROOT/<subdir>/<year>/ngo-<id>-<hash>/<filename>
"""
timestamp = timezone.now()
return "{0}/{1}/ngo-{2}-{3}/{4}".format(
subdir, timestamp.date().year, instance.pk, hash_id_secret("ngo", instance.pk), filename
)


def year_ngo_donor_directory_path(subdir: str, instance: "Donor", filename: str) -> str:
"""
The file will be uploaded to MEDIA_ROOT/<subdir>/<year>/ngo-<ngo.id>-<ngo.hash>/<id>_<hash>_<filename>
"""
timestamp = timezone.now()
return "{0}/{1}/ngo-{2}-{3}/{4}_{5}_{6}".format(
subdir,
timestamp.date().year,
instance.ngo.pk if instance.ngo else 0,
_id_code("ngo", instance.ngo.pk if instance.ngo else 0),
hash_id_secret("ngo", instance.ngo.pk if instance.ngo else 0),
instance.pk,
_id_code("donor", instance.pk),
hash_id_secret("donor", instance.pk),
filename,
)

Expand Down Expand Up @@ -156,7 +171,7 @@ class Ngo(models.Model):
blank=True,
null=False,
storage=select_public_storage,
upload_to=partial(ngo_directory_path, "prefilled_forms"),
upload_to=partial(year_ngo_directory_path, "ngo-forms"),
)

date_created = models.DateTimeField(verbose_name=_("date created"), db_index=True, auto_now_add=timezone.now)
Expand All @@ -174,8 +189,10 @@ def __str__(self):
return f"{self.name}"

def get_full_form_url(self):
if self.form_url:
return "https://{}/{}".format(settings.APEX_DOMAIN, self.form_url)
if self.prefilled_form:
return self.prefilled_form.url
elif self.form_url:
return self.form_url
else:
return ""

Expand Down Expand Up @@ -249,7 +266,7 @@ class Donor(models.Model):
verbose_name=_("PDF file"),
blank=True,
null=False,
upload_to=partial(year_ngo_donor_directory_path, "forms"),
upload_to=partial(year_ngo_donor_directory_path, "donation-forms"),
)

date_created = models.DateTimeField(verbose_name=_("date created"), db_index=True, auto_now_add=timezone.now)
Expand Down
97 changes: 81 additions & 16 deletions backend/donations/views/api.py
Original file line number Diff line number Diff line change
@@ -1,18 +1,22 @@
import json
import logging

from urllib.request import Request, urlopen
from datetime import datetime, date

from django.conf import settings
from django.contrib.auth.decorators import login_required
from django.core.exceptions import PermissionDenied, BadRequest
from django.core.files import File
from django.http import HttpResponse, JsonResponse, Http404
from django.shortcuts import redirect
from django.urls import reverse
from django.urls import reverse_lazy
from django.urls import reverse, reverse_lazy
from django.utils.decorators import method_decorator
from django.utils import timezone
from django.views.decorators.csrf import csrf_exempt

from ..models.main import Ngo
from ..models.jobs import Job
from ..models.main import Ngo, Donor, ngo_directory_path, hash_id_secret
from ..models.jobs import Job, JobStatusChoices
from ..pdf import create_pdf
from .base import BaseHandler, AccountHandler

Expand Down Expand Up @@ -93,30 +97,91 @@ def get(self, request, ngo_url, *args, **kwargs):


class GetNgoForms(AccountHandler):
def get(self, request, *args, **kwargs):
raise NotImplementedError("GetNgoForms not implemented yet")
def post(self, request, *args, **kwargs):
if not request.user.is_authenticated:
return redirect(reverse("login"))

ngo = request.user.ngo
if not ngo:
return redirect(reverse("contul-meu"))

DONATION_LIMIT = date(timezone.now().year, 5, 25)

now = timezone.now()
start_of_year = datetime(now.year, 1, 1, 0, 0)

if now.date() > DONATION_LIMIT:
return redirect(reverse("contul-meu"))

# get all the forms that have been completed since the start of the year
# and they are also signed
donations = Donor.objects.filter(ngo=ngo, date_created__gte=start_of_year, has_signed=True).all()

# extract only the urls from the array of models
urls = [u.pdf_file.url if u.pdf_file else u.pdf_url for u in donations if u.has_signed]

# if no forms
if len(urls) == 0:
logging.warn("Could not find any signed forms for this ngo: {}".format(ngo.id))
return redirect(reverse("contul-meu"))

# create job
job = Job(
ngo=ngo,
owner=request.user,
)
job.save()

export_destination = ngo_directory_path(
"exports", ngo, "export_{}_{}.zip".format(job.id, hash_id_secret(timezone.now(), job.id))
)

# make request
params = {
"passphrase": settings.ZIP_SECRET,
"urls": urls,
"path": export_destination,
"webhook": {
"url": "https://{}{}".format(settings.APEX_DOMAIN, reverse("webhook")),
"data": {"jobId": job.id},
},
}

request = Request(url=settings.ZIP_ENDPOINT, data=params, headers={"Content-type": "application/json"})

try:
httpresp = urlopen(request)
response = json.decode(httpresp.read())
logging.info(response)
httpresp.close()
except Exception as e:
logging.exception(e)
# if job failed to start remotely
job.status = JobStatusChoices.ERROR
job.save()
finally:
return redirect(reverse("contul-meu"))


@method_decorator(login_required(login_url=reverse_lazy("login")), name="dispatch")
@method_decorator(csrf_exempt, name="dispatch")
class GetUploadUrl(AccountHandler):
def post(self, request, *args, **kwargs):
files = request.FILES
if len(files) != 1:
logo_file = request.FILES.get("files")
if not logo_file:
raise BadRequest()

ngo = request.user.ngo
if not ngo:
raise BadRequest()
# # TODO: should we create the NGO here?
# ngo = Ngo.objects.create()
# ngo.save()
# request.user.ngo = ngo
# request.user.save()
ngo = Ngo.objects.create()
ngo.save()
request.user.ngo = ngo
request.user.save()

ngo.logo = files[0]
ngo.logo = logo_file
ngo.save()

self.return_json({"file_urls": [ngo.logo.url]})
return JsonResponse({"file_urls": [ngo.logo.url]})


class Webhook(BaseHandler):
Expand Down
10 changes: 4 additions & 6 deletions backend/donations/views/ngo.py
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ def post(self, request, ngo_url):
self.donor.pdf_file.delete()
self.donor.pdf_file = None

self.donor.pdf_file.save("declaratia_completata.pdf", File(new_pdf))
self.donor.pdf_file.save("declaratie_semnata.pdf", File(new_pdf))
new_pdf.close()

# # TODO: Send email
Expand Down Expand Up @@ -306,9 +306,6 @@ def get_post_value(arg, add_to_error_list=True):
# self.return_error(errors)
# return

filename = "filled_form.pdf"
pdf = create_pdf(donor_dict, ngo_data)

# create the donor and save it
donor = Donor(
first_name=donor_dict["first_name"],
Expand All @@ -324,11 +321,12 @@ def get_post_value(arg, add_to_error_list=True):
# make a request to get geo ip data for this user
# geoip = self.get_geoip_data(),
ngo=ngo,
filename=filename,
# TODO: 'filename' is unused
)
donor.save()

donor.pdf_file.save(filename, File(pdf))
pdf = create_pdf(donor_dict, ngo_data)
donor.pdf_file.save("declaratie_nesemnata.pdf", File(pdf))

# close the file after it has been uploaded
pdf.close()
Expand Down
7 changes: 4 additions & 3 deletions backend/redirectioneaza/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,8 @@
CORS_ALLOWED_ORIGINS=(list, []),
CORS_ALLOW_ALL_ORIGINS=(bool, False),
# zipping settings
ZIPPY_URL=(str, "zippy:8000"),
ZIP_ENDPOINT=(str, "zippy:8000"),
ZIP_SECRET=(str, ""),
# email settings
EMAIL_SEND_METHOD=(str, "async"),
EMAIL_BACKEND=(str, "django.core.mail.backends.console.EmailBackend"),
Expand Down Expand Up @@ -446,5 +447,5 @@

CAPTCHA_ENABLED = True if CAPTCHA_PUBLIC_KEY else False


USER_FORMS = "documents"
ZIP_ENDPOINT = env.str("ZIP_ENDPOINT")
ZIP_SECRET = env.str("ZIP_SECRET")
2 changes: 1 addition & 1 deletion backend/static_extras/js/ngo/ngo-setup.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ $(function () {
var uploadLogo = $("#upload-logo");
var displayLogo = $("#display-logo");

var aws_api_url = '/api/ngo/upload-url';
var aws_api_url = '/api/ngo/upload-url/';
var photoLogoClass = "fa-picture-o";
var loadingLogoClass = "fa-spinner fa-pulse";

Expand Down
2 changes: 1 addition & 1 deletion backend/templates/v1/all-ngos.html
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ <h1>Asociații pentru care poți redirecționa 3.5%</h1>
<div class="col-xs-6 col-sm-4 col-md-3">
<div class="ong-panel panel panel-default">
<a href="{{ ngo.slug }}">
{% set logo = ngo.logo_url if ngo.logo_url else DEFAULT_NGO_LOGO %}
{% set logo = ngo.logo.url if ngo.logo else DEFAULT_NGO_LOGO %}
<div class="ong-logo">
<img src="{{ logo }}" class="img-responsive center-block" alt="{{ ngo.name }} logo" />
</div>
Expand Down
8 changes: 4 additions & 4 deletions backend/templates/v1/components/ngo-details-form.html
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,17 @@
</div>
<div class="form-group">
<div class="col-xs-4 text-center">
<div id="upload-logo" class="{{ 'hidden' if ngo.logo_url }}">
<div id="upload-logo" class="{{ 'hidden' if ngo.logo }}">
<label for="ong-logo">
<i class="glyphicon glyphicon-picture upload-file-icon"></i>
<p>Logo-ul asociației</p>
</label>
<input type="file" id="ong-logo" class="hidden" accept="image/*" name="ong-logo" />
</div>
<div id="display-logo" class="text-center {{ 'hidden' if not ngo.logo_url }}">
<img id="ngo-logo" src="{{ ngo.logo_url if ngo }}" class="img-responsive center-block" />
<div id="display-logo" class="text-center {{ 'hidden' if not ngo.logo }}">
<img id="ngo-logo" src="{{ ngo.logo.url if ngo }}" class="img-responsive center-block" />
<span id="delete-ngo-logo" class="text-danger">Șterge</span>
<input type="hidden" id="ong-logo-url" name="ong-logo-url" value="{{ ngo.logo_url if ngo }}" />
<input type="hidden" id="ong-logo-url" name="ong-logo-url" value="{{ ngo.logo.url if ngo.logo }}" />
</div>
</div>
<div class="col-xs-8">
Expand Down
4 changes: 2 additions & 2 deletions backend/templates/v1/components/ngo-header.html
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@
<div class="col-xs-12 col-sm-10 col-sm-offset-1 col-md-8 col-md-offset-2 col-lg-6">
<div class="media ngo-details" itemscope itemtype ="http://schema.org/NGO" >
<div class="media-left">
{% if ngo.logo_url %}
<img class="media-object" itemprop="logo" src="{{ ngo.logo_url }}" />
{% if ngo.logo %}
<img class="media-object" itemprop="logo" src="{{ ngo.logo.url }}" />
{% endif %}
</div>
<div class="media-body">
Expand Down
2 changes: 1 addition & 1 deletion backend/templates/v1/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ <h4>
<div class="col-xs-6 col-sm-4 col-md-3">
<div class="ong-panel panel panel-default">
<a href="{{ ngo.slug }}">
{% set logo = ngo.logo_url if ngo.logo_url else DEFAULT_NGO_LOGO %}
{% set logo = ngo.logo.url if ngo.logo else DEFAULT_NGO_LOGO %}
<div class="ong-logo">
<img src="{{ logo }}" class="img-responsive center-block" alt="{{ ngo.name }} logo" />
</div>
Expand Down
6 changes: 3 additions & 3 deletions backend/templates/v1/twopercent.html
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@
{% block additional_headers %}
<meta property="og:title" content="{{ ngo.name }}" />
<meta property="og:description" content="{{ ngo.description }}" />
{% if ngo.logo_url %}
<meta property="og:image" content="{{ ngo.logo_url }}" />
<meta property="og:image:secure_url" content="{{ ngo.logo_url }}" />
{% if ngo.logo %}
<meta property="og:image" content="{{ ngo.logo.url }}" />
<meta property="og:image:secure_url" content="{{ ngo.logo.url }}" />
{% endif %}

<meta property="og:type" content="website" />
Expand Down
Loading