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

Fix quality #183

Merged
merged 5 commits into from
Oct 12, 2022
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
2 changes: 1 addition & 1 deletion .github/ISSUE_TEMPLATE.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,4 @@ Briefly describe the issue you are experiencing (or feature you want to see adde
* Dropdown menu option
* File uploaded (if applicable)

_PR template was adopted from [appium](https://github.com/appium/appium/blob/master/.github/ISSUE_TEMPLATE.md)_
_PR template was adopted from [appium](https://github.com/appium/appium/blob/master/.github/ISSUE_TEMPLATE.md)_
8 changes: 4 additions & 4 deletions .github/workflows/afids-validator_ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -119,16 +119,16 @@ jobs:
run: poetry install --no-interaction --no-root --with dev

- name: isort
run: poetry run isort afidsvalidator -c
run: poetry run isort afidsvalidator test -c

- name: black
run: poetry run black afidsvalidator --check
run: poetry run black afidsvalidator test --check

- name: flake8
run: poetry run flake8 afidsvalidator
run: poetry run flake8 afidsvalidator test

- name: pylint
run: poetry run pylint afidsvalidator
run: poetry run pylint afidsvalidator test

assign:
name: Reviewer assignment
Expand Down
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,4 @@ dist/
*.egg-info

# Workspaces
afids.code-workspace
afids.code-workspace
19 changes: 5 additions & 14 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
exclude: "^migrations/"
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.3.0
Expand All @@ -16,17 +17,7 @@ repos:
hooks:
- id: black

# - repo: https://github.com/pycqa/flake8
# rev: 5.0.4
# hooks:
# - id: flake8

# Pylint needs to be installed locally for precommit
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not opposed to not including the pylint in the pre-commit but just wondering if there was any particular reason for removing it?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I couldn't get it to work properly -- precommit didn't seem to play nicely with my local installation of pylint. It wouldn't pick up the pylint_flask_sqlalchemy plugin and ignored the # pylint: disable=... directives I added, leading it to fail every time (similar to what happened in the GH action).

# https://pylint.pycqa.org/en/latest/user_guide/installation/pre-commit-integration.html
# - repo: local
# hooks:
# - id: pylint
# name: pylint
# entry: pylint
# language: system
# types: [python]
- repo: https://github.com/pycqa/flake8
rev: 5.0.4
hooks:
- id: flake8
3 changes: 2 additions & 1 deletion afidsvalidator/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
from afidsvalidator.model import db, login_manager
from afidsvalidator.orcid import orcid_blueprint
from afidsvalidator.views import validator
from config import *
from config import DevelopmentConfig, ProductionConfig, TestingConfig


class ConfigException(Exception):
Expand Down Expand Up @@ -40,6 +40,7 @@ def __init__(self, message):


def create_app():
"""Create and initialize an app according to the config."""
app = Flask(__name__)

app.config.from_object(config_settings)
Expand Down
73 changes: 45 additions & 28 deletions afidsvalidator/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,17 +48,37 @@
["L olfactory sulcal fundus", "LOSF"],
]
EXPECTED_MAP = dict(zip(EXPECTED_LABELS, EXPECTED_DESCS))
FCSV_FIELDS = (
"id",
"x",
"y",
"z",
"ow",
"ox",
"oy",
"oz",
"vis",
"sel",
"lock",
"label",
"desc",
"associatedNodeID",
)


db = SQLAlchemy()
login_manager = LoginManager()


@login_manager.user_loader
def load_user(user_id):
"""Get the user with the given ID."""
return User.query.get(user_id)


class User(UserMixin, db.Model):
"""AFIDs Validator registered user."""

id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String, nullable=True)
oauths = db.relationship("OAuth", backref="user", lazy=True)
Expand All @@ -70,12 +90,18 @@ def __repr__(self):
return f"<email={self.email}>"


# pylint: disable-next=too-few-public-methods
class OAuth(OAuthConsumerMixin, db.Model):
"""Mapping from ORCID ID to internal user ID."""

user_id = db.Column(db.Integer, db.ForeignKey("user.id"), nullable=False)
provider_user_id = db.Column(db.String(20), nullable=False)


class FiducialPosition(object):
class FiducialPosition:
"""A fiducial position, in voxels."""

# pylint: disable=invalid-name
def __init__(self, x, y, z):
self.x = x
self.y = y
Expand All @@ -85,7 +111,7 @@ def __composite_values__(self):
return self.x, self.y, self.z

def __repr__(self):
return "FiducialPosition(x=%r, y=%r, z=%r)" % (self.x, self.y, self.z)
return f"FiducialPosition(x={self.x}, y={self.y}, z={self.z})"

def __eq__(self, other):
return (
Expand All @@ -102,9 +128,6 @@ def __ne__(self, other):
class FiducialSet:
"""Base class for afids."""

def __repr__(self):
return "<id {}>".format(self.id)

def serialize(self):
"""Produce a dict of each column."""
serialized = {}
Expand All @@ -115,8 +138,17 @@ def serialize(self):
return serialized

def add_fiducial(self, desc, points):
for d, p in zip(["_x", "_y", "_z"], points):
setattr(self, "".join([desc, d]), float(p))
"""Set a fiducial point's value.

Parameters
----------
desc : str
The point's description.
points: list[float] or list[str]
The x, y, and z values of the point.
"""
for suffix, val in zip(["_x", "_y", "_z"], points):
setattr(self, "".join([desc, suffix]), float(val))
setattr(
self,
desc,
Expand Down Expand Up @@ -320,6 +352,9 @@ class HumanFiducialSet(FiducialSet, db.Model):
LOSF_z = db.Column(db.Float(), nullable=False)
LOSF = composite(FiducialPosition, LOSF_x, LOSF_y, LOSF_z)

def __repr__(self):
return f"<id {self.id}>"


class InvalidFileError(Exception):
"""Exception raised when a file to be parsed is invalid.
Expand Down Expand Up @@ -404,26 +439,7 @@ def csv_to_afids(in_csv):
f"Markups fiducial file version {parsed_version} too low"
)

# Some fields irrelevant, but know they should be there
fields = (
"id",
"x",
"y",
"z",
"ow",
"ox",
"oy",
"oz",
"vis",
"sel",
"lock",
"label",
"desc",
"associatedNodeID",
)

in_csv = io.StringIO(in_csv)
csv_reader = csv.DictReader(in_csv, fields)
csv_reader = csv.DictReader(io.StringIO(in_csv), FCSV_FIELDS)

# Read csv file and dump to AFIDs object
afids = HumanFiducialSet()
Expand All @@ -439,7 +455,8 @@ def csv_to_afids(in_csv):
# Check if label is numerical
if not row_label.isdigit():
raise InvalidFileError(
f"Row label {row_label} is invalid for fiducial {expected_label}"
f"Row label {row_label} is invalid for fiducial "
f"{expected_label}"
)

expected_label += 1
Expand Down
4 changes: 2 additions & 2 deletions afidsvalidator/orcid.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,8 @@ def orcid_logged_in(blueprint, token):
else:
user = User(name=token["name"])
oauth.user = user
db.session.add_all([user, oauth])
db.session.commit()
db.session.add_all([user, oauth]) # pylint: disable=no-member
db.session.commit() # pylint: disable=no-member
login_user(user)

return False
2 changes: 1 addition & 1 deletion afidsvalidator/static/images/ORCIDiD_iconvector.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 0 additions & 1 deletion afidsvalidator/templates/contact.html
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,3 @@
</div>
</article>
{% endblock %}

1 change: 0 additions & 1 deletion afidsvalidator/templates/db.html
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,3 @@
</table>
</section>
{% endblock %}

1 change: 0 additions & 1 deletion afidsvalidator/templates/login.html
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,3 @@
</div>
</div>
{% endblock %}

52 changes: 22 additions & 30 deletions afidsvalidator/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import os
from datetime import datetime, timezone
from pathlib import Path

import numpy as np
import wtforms as wtf
Expand Down Expand Up @@ -32,6 +33,7 @@


class Average(wtf.Form):
# pylint: disable=too-few-public-methods
"""Form for selecting and submitting a file."""

filename = wtf.FileField(validators=[wtf.validators.InputRequired()])
Expand Down Expand Up @@ -80,6 +82,7 @@ def logout():


# Validator
# pylint: disable=no-member
@validator.route("/app.html", methods=["GET", "POST"])
def validate():
"""Present the validator form, or validate an AFIDs set."""
Expand All @@ -95,11 +98,6 @@ def validate():
form_choices = [choice.capitalize() for choice in form_choices]
form_choices.sort()

# Get time stamp
timestamp = str(
datetime.now(timezone.utc).strftime("%Y-%m-%d %H:%M:%S %Z")
)

if not (request.method == "POST" and request.files):
return render_template(
"app.html",
Expand All @@ -116,7 +114,7 @@ def validate():
upload_ext, file_check = allowed_file(upload.filename)

if not (upload and file_check):
result = f"Invalid file: extension not allowed ({timestamp})"
result = "Invalid file: extension not allowed"

return render_template(
"app.html",
Expand All @@ -135,7 +133,7 @@ def validate():
else:
user_afids = json_to_afids(upload.read().decode("utf-8"))
except InvalidFileError as err:
result = f"Invalid file: {err.message} ({timestamp})"
result = f"Invalid file: {err.message}"
return render_template(
"app.html",
form=form,
Expand All @@ -149,14 +147,10 @@ def validate():
)

if user_afids.validate():
result = f"Valid file ({timestamp})"
result = "Valid file"
else:
result = (
f"Invalid AFIDs set, please double check your file ({timestamp})"
)
result = "Invalid AFIDs set, please double check your file"

fid_species = request.form["fid_species"]
fid_species = fid_species.lower()
fid_template = request.form["fid_template"]

if fid_template == "Validate file structure":
Expand All @@ -174,9 +168,14 @@ def validate():

result = f"{result}<br>{fid_template} selected"
# Need to pull from correct folder when more templates are added
template_file_path = f"{current_app.config['AFIDS_DIR']}/{fid_species.lower()}/tpl-{fid_template}_afids.fcsv"

with open(template_file_path, "r") as template_file:
with open(
Path(current_app.config["AFIDS_DIR"])
/ request.form["fid_species"].lower()
/ f"tpl-{fid_template}_afids.fcsv",
"r",
encoding="utf-8",
) as template_file:
template_afids = csv_to_afids(template_file.read())

if request.form.get("db_checkbox"):
Expand All @@ -189,22 +188,13 @@ def validate():
print("DB option unchecked, user data not saved")

for desc in EXPECTED_DESCS:
distances.append(
"{:.5f}".format(
np.linalg.norm(
np.array(
[
getattr(
template_afids, desc[-1]
).__composite_values__()
]
)
- np.array(
[getattr(user_afids, desc[-1]).__composite_values__()]
)
)
distance = np.linalg.norm(
np.array(
[getattr(template_afids, desc[-1]).__composite_values__()]
)
- np.array([getattr(user_afids, desc[-1]).__composite_values__()])
)
distances.append(f"{distance:.5f}")
labels.append(desc[-1])

return render_template(
Expand All @@ -216,7 +206,9 @@ def validate():
index=list(range(len(EXPECTED_DESCS))),
labels=labels,
distances=distances,
timestamp=timestamp,
timestamp=str(
datetime.now(timezone.utc).strftime("%Y-%m-%d %H:%M:%S %Z")
),
scatter_html=generate_3d_scatter(template_afids, user_afids),
histogram_html=generate_histogram(template_afids, user_afids),
current_user=current_user,
Expand Down
Loading