Extract Spreadsheet model from app/utils.py

`app/utils.py` is a bit of a dumping ground for things we don’t have a
better place for.

We now have a place and structure for storing ‘model’ code (‘model’ in
the model, view, controller (MVC) sense of the word).

This commit moves the spreadsheet model to that place.
This commit is contained in:
Chris Hill-Scott
2021-01-06 12:31:39 +00:00
parent 1def62c497
commit 2c46d023da
4 changed files with 110 additions and 106 deletions

View File

@@ -1,20 +1,15 @@
import csv
import os
import re
import unicodedata
from datetime import datetime, timedelta, timezone
from functools import wraps
from io import BytesIO, StringIO
from itertools import chain
from math import floor, log10
from numbers import Number
from os import path
from urllib.parse import urlparse
import ago
import dateutil
import pyexcel
import pyexcel_xlsx
import pytz
from dateutil import parser
from flask import (
@@ -53,6 +48,7 @@ from orderedset._orderedset import OrderedSet
from werkzeug.datastructures import MultiDict
from werkzeug.routing import RequestRedirect
from app.models.spreadsheet import Spreadsheet
from app.notify_client.organisations_api_client import organisations_client
SENDING_STATUSES = ['created', 'pending', 'sending', 'pending-virus-check']
@@ -289,105 +285,6 @@ def id_safe(string):
return email_safe(string, whitespace='-')
class Spreadsheet():
ALLOWED_FILE_EXTENSIONS = ('csv', 'xlsx', 'xls', 'ods', 'xlsm', 'tsv')
def __init__(self, csv_data=None, rows=None, filename=''):
self.filename = filename
if csv_data and rows:
raise TypeError('Spreadsheet must be created from either rows or CSV data')
self._csv_data = csv_data or ''
self._rows = rows or []
@property
def as_dict(self):
return {
'file_name': self.filename,
'data': self.as_csv_data
}
@property
def as_csv_data(self):
if not self._csv_data:
with StringIO() as converted:
output = csv.writer(converted)
for row in self._rows:
output.writerow(row)
self._csv_data = converted.getvalue()
return self._csv_data
@classmethod
def can_handle(cls, filename):
return cls.get_extension(filename) in cls.ALLOWED_FILE_EXTENSIONS
@staticmethod
def get_extension(filename):
return path.splitext(filename)[1].lower().lstrip('.')
@staticmethod
def normalise_newlines(file_content):
return '\r\n'.join(file_content.read().decode('utf-8').splitlines())
@classmethod
def from_rows(cls, rows, filename=''):
return cls(rows=rows, filename=filename)
@classmethod
def from_dict(cls, dictionary, filename=''):
return cls.from_rows(
zip(
*sorted(dictionary.items(), key=lambda pair: pair[0])
),
filename=filename,
)
@classmethod
def from_file(cls, file_content, filename=''):
extension = cls.get_extension(filename)
if extension == 'csv':
return cls(csv_data=Spreadsheet.normalise_newlines(file_content), filename=filename)
if extension == 'tsv':
file_content = StringIO(
Spreadsheet.normalise_newlines(file_content))
instance = cls.from_rows(
pyexcel.iget_array(
file_type=extension,
file_stream=file_content),
filename)
pyexcel.free_resources()
return instance
@classmethod
def from_file_form(cls, form):
return cls.from_file(
form.file.data,
filename=form.file.data.filename,
)
@property
def as_rows(self):
if not self._rows:
self._rows = list(csv.reader(
StringIO(self._csv_data),
quoting=csv.QUOTE_MINIMAL,
skipinitialspace=True,
))
return self._rows
@property
def as_excel_file(self):
io = BytesIO()
pyexcel_xlsx.save_data(io, {'Sheet 1': self.as_rows})
return io.getvalue()
def get_help_argument():
return request.args.get('help') if request.args.get('help') in ('1', '2', '3') else None