Skip to content

Commit

Permalink
Merge branch 'main' into shortlink-form
Browse files Browse the repository at this point in the history
  • Loading branch information
vidya-ram committed Nov 3, 2023
2 parents 086ad1f + 683cf9d commit badd758
Show file tree
Hide file tree
Showing 58 changed files with 1,249 additions and 1,054 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/pytest.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ jobs:
fail-fast: false
matrix:
os: [ubuntu-latest] # TODO: Figure out macos-latest and Docker
python-version: ['3.11']
python-version: ['3.11', '3.12']

services:
redis:
Expand Down Expand Up @@ -81,7 +81,7 @@ jobs:
- name: Install Node
uses: actions/setup-node@v3
with:
node-version: latest
node-version: 20
cache: npm
- name: Cache node modules
uses: actions/cache@v3
Expand Down
12 changes: 7 additions & 5 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -44,15 +44,17 @@ repos:
'PYSEC-2023-73', # https://github.com/RedisLabs/redisraft/issues/608
'--ignore-vuln',
'PYSEC-2023-101', # https://github.com/pytest-dev/pytest-selenium/issues/310
'--ignore-vuln',
'PYSEC-2023-206', # pytest-selenium again
]
files: ^requirements/.*\.txt$
- repo: https://github.com/asottile/pyupgrade
rev: v3.13.0
rev: v3.15.0
hooks:
- id: pyupgrade
args: ['--keep-runtime-typing', '--py310-plus']
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.0.291
rev: v0.1.3
hooks:
- id: ruff
args: ['--fix', '--exit-non-zero-on-fix']
Expand Down Expand Up @@ -98,7 +100,7 @@ repos:
additional_dependencies:
- tomli
- repo: https://github.com/psf/black
rev: 23.9.1
rev: 23.10.1
hooks:
- id: black
# Mypy is temporarily disabled until the SQLAlchemy 2.0 migration is complete
Expand Down Expand Up @@ -127,7 +129,7 @@ repos:
- id: flake8
additional_dependencies: *flake8deps
- repo: https://github.com/PyCQA/pylint
rev: v3.0.0a7
rev: v3.0.1
hooks:
- id: pylint
args: [
Expand All @@ -147,7 +149,7 @@ repos:
additional_dependencies:
- 'bandit[toml]'
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.4.0
rev: v4.5.0
hooks:
- id: check-added-large-files
- id: check-ast
Expand Down
7 changes: 4 additions & 3 deletions funnel/assets/js/rsvp_form_modal.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,14 @@ import jsonForm from './utils/jsonform';
Vue.config.devtools = true;

const FormUI = {
init(jsonSchema) {
init(jsonSchema, useremail) {
/* eslint-disable no-new */
new Vue({
el: '#register-form',
data() {
return {
jsonSchema,
useremail,
};
},
components: {
Expand All @@ -27,7 +28,7 @@ const FormUI = {
};

$(() => {
window.Hasgeek.addRsvpForm = (jsonSchema) => {
FormUI.init(jsonSchema);
window.Hasgeek.addRsvpForm = (jsonSchema, useremail) => {
FormUI.init(jsonSchema, useremail);
};
});
2 changes: 1 addition & 1 deletion funnel/assets/js/utils/jsonform.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import Form from './formhelper';

const jsonForm = Vue.component('jsonform', {
template: '#form-template',
props: ['jsonschema', 'title', 'formid'],
props: ['jsonschema', 'title', 'formid', 'useremail'],
methods: {
getFormData() {
const obj = {};
Expand Down
2 changes: 1 addition & 1 deletion funnel/assets/js/utils/ractive_util.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { USER_AVATAR_IMG_SIZE } from '../constants';
Ractive.DEBUG = false;

export const useravatar = Ractive.extend({
template: `{{#if user.profile_url && addprofilelink }}<a href="{{user.profile_url}}" class="nounderline">{{#if user.logo_url }}<img class="user__box__gravatar" src="{{ imgurl() }}" />{{else}}<div class="user__box__gravatar user__box__gravatar--initials" data-avatar-colour="{{ getAvatarColour(user.fullname) }}">{{ getInitials(user.fullname) }}</div>{{/if}}</a>{{else}}<span>{{#if user.logo_url }}<img class="user__box__gravatar" src="{{ imgurl() }}" />{{else}}<div class="user__box__gravatar user__box__gravatar--initials" data-avatar-colour="{{ getAvatarColour(user.fullname) }}">{{ getInitials(user.fullname) }}</div>{{/if}}</span>{{/if}}`,
template: `{{#if addprofilelink }}<a href="{{user.absolute_url}}" class="nounderline">{{#if user.logo_url }}<img class="user__box__gravatar" src="{{ imgurl() }}" />{{else}}<div class="user__box__gravatar user__box__gravatar--initials" data-avatar-colour="{{ getAvatarColour(user.fullname) }}">{{ getInitials(user.fullname) }}</div>{{/if}}</a>{{else}}<span>{{#if user.logo_url }}<img class="user__box__gravatar" src="{{ imgurl() }}" />{{else}}<div class="user__box__gravatar user__box__gravatar--initials" data-avatar-colour="{{ getAvatarColour(user.fullname) }}">{{ getInitials(user.fullname) }}</div>{{/if}}</span>{{/if}}`,
data: {
addprofilelink: true,
size: 'medium',
Expand Down
2 changes: 1 addition & 1 deletion funnel/assets/js/utils/vue_util.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { USER_AVATAR_IMG_SIZE } from '../constants';

export const userAvatarUI = Vue.component('useravatar', {
template:
'<a :href="user.profile_url" v-if="user.profile_url && addprofilelink" class="nounderline"><span class="user__box__wrapper" v-if="user.logo_url"><img class="user__box__gravatar" :src="imgurl"></span><div class="user__box__gravatar user__box__gravatar--initials" :data-avatar-colour="getAvatarColour(user.fullname)" v-else>{{ getInitials(user.fullname) }}</div></a v-if="user.profile_url && addprofilelink"></a><span v-else><img class="user__box__gravatar" :src="imgurl" v-if="user.logo_url"/><div class="user__box__gravatar user__box__gravatar--initials" :data-avatar-colour="getAvatarColour(user.fullname)" v-else>{{ getInitials(user.fullname) }}</span v-else>',
'<a :href="user.absolute_url" v-if="addprofilelink" class="nounderline"><span class="user__box__wrapper" v-if="user.logo_url"><img class="user__box__gravatar" :src="imgurl"></span><div class="user__box__gravatar user__box__gravatar--initials" :data-avatar-colour="getAvatarColour(user.fullname)" v-else>{{ getInitials(user.fullname) }}</div></a v-if="user.absolute_url && addprofilelink"></a><span v-else><img class="user__box__gravatar" :src="imgurl" v-if="user.logo_url"/><div class="user__box__gravatar user__box__gravatar--initials" :data-avatar-colour="getAvatarColour(user.fullname)" v-else>{{ getInitials(user.fullname) }}</span v-else>',
props: {
user: Object,
addprofilelink: {
Expand Down
2 changes: 1 addition & 1 deletion funnel/assets/sass/components/_ticket-modal.scss
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@
}

.price-btn {
min-width: 200px;
min-width: 150px;
font-size: inherit;
padding: 0;
display: flex;
Expand Down
9 changes: 6 additions & 3 deletions funnel/forms/organization.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,9 +61,12 @@ def validate_name(self, field: forms.Field) -> None:
)
if reason == 'reserved':
raise forms.validators.ValidationError(_("This name is reserved"))
if self.edit_obj and field.data.lower() == self.edit_obj.name.lower():
# Name is not reserved or invalid under current rules. It's also not changed
# from existing name, or has only changed case. This is a validation pass.
if (
self.edit_obj
and self.edit_obj.name
and field.data.lower() == self.edit_obj.name.lower()
):
# Name has only changed case from previous name. This is a validation pass
return
if reason == 'user':
if (
Expand Down
7 changes: 5 additions & 2 deletions funnel/forms/sync_ticket.py
Original file line number Diff line number Diff line change
Expand Up @@ -175,8 +175,8 @@ class TicketParticipantForm(forms.Form):
)
email = forms.EmailField(
__("Email"),
validators=[forms.validators.DataRequired(), forms.validators.ValidEmail()],
filters=[forms.filters.strip()],
validators=[forms.validators.Optional(), forms.validators.ValidEmail()],
filters=[forms.filters.none_if_empty()],
)
phone = forms.StringField(
__("Phone number"),
Expand Down Expand Up @@ -219,6 +219,9 @@ def set_queries(self) -> None:
def validate(self, *args, **kwargs) -> bool:
"""Validate form."""
result = super().validate(*args, **kwargs)
if self.email.data is None:
self.user = None
return True
with db.session.no_autoflush:
accountemail = AccountEmail.get(email=self.email.data)
if accountemail is not None:
Expand Down
32 changes: 14 additions & 18 deletions funnel/models/account.py
Original file line number Diff line number Diff line change
Expand Up @@ -113,9 +113,14 @@ class PROFILE_STATE(LabeledEnum): # noqa: N801
class ZBase32Comparator(Comparator[str]): # pylint: disable=abstract-method
"""Comparator to allow lookup by Account.uuid_zbase32."""

def __eq__(self, other: str) -> sa.ColumnElement[bool]: # type: ignore[override]
def __eq__(self, other: object) -> sa.ColumnElement[bool]: # type: ignore[override]
"""Return an expression for column == other."""
return self.__clause_element__() == UUID(bytes=zbase32_decode(other))
try:
return self.__clause_element__() == UUID( # type: ignore[return-value]
bytes=zbase32_decode(str(other))
)
except ValueError: # zbase32 call failed, so it's not a valid string
return sa.false()


class Account(UuidMixin, BaseMixin, Model):
Expand Down Expand Up @@ -245,9 +250,7 @@ class Account(UuidMixin, BaseMixin, Model):
#: Verified accounts get listed on the home page and are not considered throwaway
#: accounts for spam control. There are no other privileges at this time
is_verified: Mapped[bool] = with_roles(
immutable(
sa.orm.mapped_column(sa.Boolean, default=False, nullable=False, index=True)
),
sa.orm.mapped_column(sa.Boolean, default=False, nullable=False, index=True),
read={'all'},
)

Expand Down Expand Up @@ -332,7 +335,7 @@ class Account(UuidMixin, BaseMixin, Model):
'logo_url',
'banner_image_url',
'joined_at',
'profile_url',
'absolute_url',
'urls',
'is_user_profile',
'is_organization_profile',
Expand All @@ -357,7 +360,7 @@ class Account(UuidMixin, BaseMixin, Model):
'logo_url',
'website',
'joined_at',
'profile_url',
'absolute_url',
'is_verified',
},
'related': {
Expand All @@ -373,7 +376,7 @@ class Account(UuidMixin, BaseMixin, Model):
'description',
'logo_url',
'joined_at',
'profile_url',
'absolute_url',
'is_verified',
},
}
Expand Down Expand Up @@ -618,13 +621,6 @@ def has_public_profile(self) -> bool:

with_roles(has_public_profile, read={'all'}, write={'owner'})

@property
def profile_url(self) -> str | None:
"""Return optional URL to account profile page."""
return self.url_for(_external=True)

with_roles(profile_url, read={'all'})

def is_profile_complete(self) -> bool:
"""Verify if profile is complete (fullname, username and contacts present)."""
return bool(self.title and self.name and self.has_verified_contact_info)
Expand Down Expand Up @@ -1292,7 +1288,7 @@ class DuckTypeAccount(RoleMixin):
uuid_b58: None = None
username: None = None
name: None = None
profile_url: None = None
absolute_url: None = None
email: None = None
phone: None = None

Expand All @@ -1313,7 +1309,7 @@ class DuckTypeAccount(RoleMixin):
'username',
'fullname',
'pickername',
'profile_url',
'absolute_url',
},
'call': {'views', 'forms', 'features', 'url_for'},
}
Expand All @@ -1324,7 +1320,7 @@ class DuckTypeAccount(RoleMixin):
'username',
'fullname',
'pickername',
'profile_url',
'absolute_url',
}
}

Expand Down
14 changes: 4 additions & 10 deletions funnel/models/comment.py
Original file line number Diff line number Diff line change
Expand Up @@ -278,16 +278,16 @@ class Comment(UuidMixin, BaseMixin, Model):

__roles__ = {
'all': {
'read': {'created_at', 'urls', 'uuid_b58', 'has_replies'},
'read': {'created_at', 'urls', 'uuid_b58', 'has_replies', 'absolute_url'},
'call': {'state', 'commentset', 'view_for', 'url_for'},
},
'replied_to_commenter': {'granted_via': {'in_reply_to': '_posted_by'}},
}

__datasets__ = {
'primary': {'created_at', 'urls', 'uuid_b58'},
'related': {'created_at', 'urls', 'uuid_b58'},
'json': {'created_at', 'urls', 'uuid_b58'},
'primary': {'created_at', 'urls', 'uuid_b58', 'absolute_url'},
'related': {'created_at', 'urls', 'uuid_b58', 'absolute_url'},
'json': {'created_at', 'urls', 'uuid_b58', 'absolute_url'},
'minimal': {'created_at', 'uuid_b58'},
}

Expand Down Expand Up @@ -361,12 +361,6 @@ def _message_expression(cls):
message, read={'all'}, datasets={'primary', 'related', 'json', 'minimal'}
)

@property
def absolute_url(self) -> str:
return self.url_for()

with_roles(absolute_url, read={'all'}, datasets={'primary', 'related', 'json'})

@property
def title(self) -> str:
obj = self.commentset.parent
Expand Down
4 changes: 2 additions & 2 deletions funnel/models/notification_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -248,7 +248,7 @@ class RegistrationReceivedNotification(


class OrganizationAdminMembershipNotification(
DocumentHasAccount,
DocumentIsAccount,
Notification[Account, AccountMembership],
type='organization_membership_granted',
):
Expand All @@ -263,7 +263,7 @@ class OrganizationAdminMembershipNotification(


class OrganizationAdminMembershipRevokedNotification(
DocumentHasAccount,
DocumentIsAccount,
Notification[Account, AccountMembership],
type='organization_membership_revoked',
shadows=OrganizationAdminMembershipNotification,
Expand Down
5 changes: 4 additions & 1 deletion funnel/models/project_membership.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
from .membership_mixin import ImmutableUserMembershipMixin
from .project import Project

__all__ = ['ProjectMembership', 'project_child_role_map']
__all__ = ['ProjectMembership', 'project_child_role_map', 'project_child_role_set']

#: Roles in a project and their remapped names in objects attached to a project
project_child_role_map: dict[str, str] = {
Expand All @@ -24,6 +24,9 @@
'reader': 'reader',
}

#: A model that is indirectly under a project needs the role names without remapping
project_child_role_set: set[str] = set(project_child_role_map.values())

#: ProjectMembership maps project's `account_admin` role to membership's `editor`
#: role in addition to the recurring role grant map
project_membership_role_map: dict[str, str | set[str]] = {
Expand Down
4 changes: 2 additions & 2 deletions funnel/models/rsvp.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ class Rsvp(UuidMixin, NoIdMixin, Model):
project_id = sa.orm.mapped_column(
sa.Integer, sa.ForeignKey('project.id'), nullable=False, primary_key=True
)
project = with_roles(
project: Mapped[Project] = with_roles(
relationship(Project, backref=backref('rsvps', cascade='all', lazy='dynamic')),
read={'owner', 'project_promoter'},
grants_via={None: project_child_role_map},
Expand All @@ -54,7 +54,7 @@ class Rsvp(UuidMixin, NoIdMixin, Model):
participant_id: Mapped[int] = sa.orm.mapped_column(
sa.ForeignKey('account.id'), nullable=False, primary_key=True
)
participant = with_roles(
participant: Mapped[Account] = with_roles(
relationship(Account, backref=backref('rsvps', cascade='all', lazy='dynamic')),
read={'owner', 'project_promoter'},
grants={'owner'},
Expand Down
15 changes: 10 additions & 5 deletions funnel/models/sync_ticket.py
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,9 @@ class TicketEvent(GetTitleMixin, Model):
'read': {'name', 'title'},
'write': {'name', 'title'},
},
'project_usher': {
'read': {'name', 'title'},
},
}


Expand Down Expand Up @@ -207,7 +210,7 @@ class TicketParticipant(EmailAddressMixin, UuidMixin, BaseMixin, Model):
"""A participant in one or more events, synced from an external ticket source."""

__tablename__ = 'ticket_participant'
__email_optional__ = False
__email_optional__ = True
__email_for__ = 'participant'

fullname = with_roles(
Expand Down Expand Up @@ -297,10 +300,10 @@ def has_public_profile(self) -> bool:
with_roles(has_public_profile, read={'all'})

@property
def profile_url(self) -> str | None:
return self.participant.profile_url if self.participant else None
def absolute_url(self) -> str | None:
return self.participant.absolute_url if self.participant else None

with_roles(profile_url, read={'all'})
with_roles(absolute_url, read={'all'})

@classmethod
def get(
Expand Down Expand Up @@ -376,7 +379,9 @@ def checkin_list(cls, ticket_event: TicketEvent) -> list: # TODO: List type?
TicketEventParticipant,
TicketParticipant.id == TicketEventParticipant.ticket_participant_id,
)
.join(EmailAddress, EmailAddress.id == TicketParticipant.email_address_id)
.outerjoin(
EmailAddress, EmailAddress.id == TicketParticipant.email_address_id
)
.outerjoin(
SyncTicket, TicketParticipant.id == SyncTicket.ticket_participant_id
)
Expand Down
Loading

0 comments on commit badd758

Please sign in to comment.