Skip to content

Commit

Permalink
Merge remote-tracking branch 'upstream/development' into development
Browse files Browse the repository at this point in the history
  • Loading branch information
vtestagrossa committed Feb 19, 2024
2 parents b919d7a + 164d579 commit 334ea46
Show file tree
Hide file tree
Showing 17 changed files with 173 additions and 39 deletions.
4 changes: 2 additions & 2 deletions .idea/hindsite.iml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 0 additions & 1 deletion app/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +0,0 @@

4 changes: 2 additions & 2 deletions app/hindsite/auth/authenticate_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ def register_user(email: str, password: str):
:param email:
:param password:
:return: **str** Returns a string indicating an error, or None if there is no error.
:return: **User** Returns the user created in registration.
:raises RegistrationError: Raises this in case of an already extant account or if the password
is not a valid secret.
Expand All @@ -108,7 +108,7 @@ def register_user(email: str, password: str):
new_user.password = [new_user_pass]
db.session.add(new_user)
db.session.commit()
return True
return new_user


def valid_secret(secret: str):
Expand Down
3 changes: 2 additions & 1 deletion app/hindsite/common_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
from app.hindsite.extensions import db
from app.hindsite.tables import User


def get_user(email: str):
"""
Gets a single user record.
Expand All @@ -18,4 +19,4 @@ def get_user(email: str):
user = db.session.execute(stmt).first()
if user is not None:
return db.session.execute(stmt).first()[0]
return None
return None
16 changes: 6 additions & 10 deletions app/hindsite/extensions.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,31 +3,27 @@
"""
import flask_login
from flask_bootstrap import Bootstrap5
from sqlalchemy import Table, Column, ForeignKey
from typing_extensions import Annotated
import sqlalchemy.orm
from sqlalchemy.orm import DeclarativeBase, MappedAsDataclass
from flask_sqlalchemy import SQLAlchemy

# BOOTSTRAP INSTANTIATION

bootstrap = Bootstrap5()

# FLASK_LOGIN LOGIN MANAGER INSTANTIATION.

login_manager = flask_login.LoginManager()

# SQLALCHEMY EXTENSION SET-UP.


class Base(DeclarativeBase, MappedAsDataclass): # pylint: disable=too-few-public-methods
"""
The base model for the classes to be declared.
"""


# Defines association table for the User/Group relationship
user_membership = Table(
'user_membership',
Base.metadata,
Column('user_id', ForeignKey('user.id'), primary_key=True),
Column('group_id', ForeignKey('hindsite_group.id'), primary_key=True)
)

# pylint: disable=invalid-name
intpk = Annotated[int, sqlalchemy.orm.mapped_column(primary_key=True,
autoincrement=True)]
Expand Down
2 changes: 2 additions & 0 deletions app/hindsite/group/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,6 @@

bp = Blueprint('home', __name__)

# pylint: disable=wrong-import-position
from app.hindsite.group import group
# pylint: enable=wrong-import-position
2 changes: 2 additions & 0 deletions app/hindsite/home/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,6 @@

bp = Blueprint('home', __name__)

# pylint: disable=wrong-import-position
from app.hindsite.home import home
# pylint: enable=wrong-import-position
73 changes: 64 additions & 9 deletions app/hindsite/home/models.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
"""
Defines logic flow for the authentication process, additionally manages flask-login
session management.
Defines logic adding and creating groups.
"""

from sqlalchemy import select
from app.hindsite.extensions import db
from app.hindsite.common_model import get_user
from app.hindsite.tables import Group
from app.hindsite.tables import Group, Membership


class GroupAddError(Exception):
"""
Expand All @@ -18,22 +18,69 @@ class GroupAddError(Exception):
def __init__(self, message):
self.message = message


def get_groups(email: str):
"""
Gets all group records belonging to a user.
:param email: **str** Email to check against the database
:returns: **groups** or **None**
:returns: **list** A list of the user's current groups.
"""
user = get_user(email)
groups = user.groups
groups = []
for membership in user.groups:
if membership.invitation_accepted is True:
groups.append(membership.group)
return groups


def get_invitations(email: str):
"""
Looks at memberships matching the user with the supplied email and
returns all invitations that are currently active.
:param email: Email of the user being checked for invitations.
:returns: **List** A list of Memberships for invitations.
"""
user = get_user(email)
invitations = []
for membership in user.groups:
if membership.invitation_accepted is False:
invitations.append(membership)
return invitations


def accept_invitation(membership: Membership):
"""
Accepts an invitation to a group by setting the flag for <code>invitation_accepted</code>
to True
"""
membership.invitation_accepted = True
db.session.commit()


def send_invitation(group_id: int, email: str):
"""
Creates a Membership that signals an invitation to a user, by default the membership
is not an ownership membership and will have <code>invitation_accepted</code> set to False
:param group_id: The id of the group attached to the membership.
:param email: The email of the user to be added to the membership.
:returns: **Membership** A reference to the membership object.
"""
user = get_user(email)
group = get_group(group_id)
membership = Membership(user, group)
db.session.add(membership)
db.session.commit()
return membership


def get_group(group_id: int):
"""
Gets a group record belonging to a user.
:param id: **int** id to check against the database
:param group_id: **int** id to check against the database
:returns: **group** or **None**
"""
stmt = select(Group).where(Group.id == group_id)
Expand All @@ -42,14 +89,22 @@ def get_group(group_id: int):
return db.session.execute(stmt).first()[0]
return None


def create_group(name: str, email: str):
"""
Creates the group and associates the group with the current user
Creates the group and associates the group with the current user. The creating
user is labeled as the owner of the group.
:param name: The name of the group to be created.
:param email: The email of the user to be added to the group.
:returns: **Group** The newly created group.
"""
user = get_user(email)
group = add_group(name)
user.groups.append(group)
db.session.add(user)
membership = Membership(user, group)
membership.owner = True
membership.invitation_accepted = True
db.session.add(membership)
db.session.commit()
return group

Expand Down
1 change: 1 addition & 0 deletions app/hindsite/tables/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from .user import User
from .password import Password
from .group import Group
from .membership import Membership
from .board import Board
from .field import Field
from .card import Card
12 changes: 10 additions & 2 deletions app/hindsite/tables/board.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,22 @@
class Board(db.Model): # pylint: disable=too-few-public-methods
"""
The Board table acts as an aggregate to fields.
Attributes:
id: **int** Primary key for the board
group_id: **int** Foreign key referencing the group the board belongs to.
timer: **datetime** Target time that timer is counting down to.
end_time: **datetime** Time the board's retrospective ended.
start_time: **datetime** Time the board's restrospective started.
archived: **bool** Whether the board is 'deleted'.
groups: **Group** The group the board belongs to.
fields: **Field** Fields under this board.
"""
__tablename__ = 'board'

id: Mapped[intpk] = mapped_column(init=False)
group_id: Mapped[int] = mapped_column(ForeignKey('hindsite_group.id'))

timer: Mapped[datetime.datetime] = mapped_column(DateTime(timezone=True))

end_time: Mapped[datetime.datetime] = mapped_column(DateTime(timezone=True))
start_time: Mapped[datetime.datetime] = mapped_column(DateTime(timezone=True),
default=datetime.datetime.now())
Expand Down
13 changes: 13 additions & 0 deletions app/hindsite/tables/card.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,19 @@
class Card(db.Model): # pylint: disable=too-few-public-methods
"""
The Card holds comments and reactions from other users in the application.
Attributes:
id: **int** Primary key for the card
field_id: **int** Foreign key referencing the field this card belongs to
author_id: **int** Foreign key referencing the user who created the card
owner_id: **int** Foreign key (optional) referencing the user who is
charged with the issue/content of the card.
message_body: **str** The body of the content in the card.
archived: **bool** Whether the card has been 'deleted'.
field: **Field** Field that the card belongs to.
author: **User** Author of the card.
owner: **User** Assigned owner of the issue/subject in the card.
"""
__tablename__ = 'card'

Expand Down
8 changes: 8 additions & 0 deletions app/hindsite/tables/field.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,14 @@
class Field(db.Model): # pylint: disable=too-few-public-methods
"""
The field table is an archivable reference between boards and cards.
Attributes:
id: **int** Primary Key of the field.
board_id: **int** Foreign Key referencing the board this field belongs to.
name: **str** Name of the field.
archived: **bool** Whether the field has been 'deleted'.
board: **Board** The board this field belongs to.
cards: **Card** Cards under this field.
"""
__tablename__ = 'field'

Expand Down
6 changes: 4 additions & 2 deletions app/hindsite/tables/group.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
"""
Class definition for the group table
"""
from typing import List

from sqlalchemy import String
from sqlalchemy.orm import Mapped, mapped_column, relationship
from app.hindsite.extensions import db, intpk, user_membership
from app.hindsite.extensions import db, intpk


class Group(db.Model): # pylint: disable=too-few-public-methods
Expand All @@ -19,5 +21,5 @@ class Group(db.Model): # pylint: disable=too-few-public-methods

id: Mapped[intpk] = mapped_column(init=False)
name: Mapped[str] = mapped_column(String(50))
users = relationship('User', secondary=user_membership, back_populates='groups')
users: Mapped[List['Membership']] = relationship(back_populates='group', init=False)
boards = relationship('Board', back_populates='groups')
23 changes: 23 additions & 0 deletions app/hindsite/tables/membership.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
"""
Association table for groups and users.
"""
from sqlalchemy import ForeignKey
from sqlalchemy.orm import Mapped, mapped_column, relationship

from app.hindsite.extensions import db


class Membership(db.Model): # pylint: disable=too-few-public-methods
"""
Associates user and groups.
"""
__tablename__ = 'membership'

user_id: Mapped[int] = mapped_column(ForeignKey('user.id'), primary_key=True,
init=False)
group_id: Mapped[int] = mapped_column(ForeignKey('hindsite_group.id'), primary_key=True,
init=False)
user: Mapped['User'] = relationship(back_populates='groups')
group: Mapped['Group'] = relationship(back_populates='users')
owner: Mapped[bool] = mapped_column(default=False)
invitation_accepted: Mapped[bool] = mapped_column(default=False)
9 changes: 5 additions & 4 deletions app/hindsite/tables/user.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@
"""
import datetime

from typing import Optional
from typing import Optional, List
from sqlalchemy import DateTime, String
from sqlalchemy.orm import Mapped, mapped_column, relationship
from app.hindsite.extensions import db, intpk, user_membership
from app.hindsite.extensions import db, intpk


class User(db.Model): # pylint: disable=too-few-public-methods
Expand All @@ -22,6 +22,7 @@ class User(db.Model): # pylint: disable=too-few-public-methods
last_login: **datetime** Datetime of the User's last login into the system.
verified: **bool** Whether or not the user's email address has been verified.
password: **Password** Reference to password table entry.
groups: **Group** Groups that user currently belongs to.
"""
__tablename__ = 'user'

Expand All @@ -30,8 +31,8 @@ class User(db.Model): # pylint: disable=too-few-public-methods
last_name: Mapped[Optional[str]] = mapped_column(String(50), init=False)
display_name: Mapped[Optional[str]] = mapped_column(String(50))
email: Mapped[str] = mapped_column(String(50), unique=True)
password = relationship("Password", back_populates='user')
groups: Mapped[List['Membership']] = relationship(back_populates='user', init=False)
last_login: Mapped[datetime.datetime] = mapped_column(
DateTime(timezone=True), default=datetime.datetime.now())
verified: Mapped[bool] = mapped_column(default=False)
password = relationship("Password", back_populates='user')
groups = relationship('Group', secondary=user_membership, back_populates='users')
23 changes: 23 additions & 0 deletions app/test_data.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
"""
Test data function to populate the table with tables.
"""

from app.hindsite.extensions import db
from app.hindsite.auth.authenticate_model import register_user


def populate_database(app):
"""
Function that wipes and populates the database.
:param app: Running flask app.
"""
with app.app_context():
db.drop_all()
db.create_all()

register_user('[email protected]',
'Iaintafraid_ofnuthin1')
register_user('[email protected]',
'Ialwaysaintafraid_ofnuthin1')
register_user('[email protected]',
'Isometimesaintafraid_ofnuthin1')
Loading

0 comments on commit 334ea46

Please sign in to comment.