-
Notifications
You must be signed in to change notification settings - Fork 4
RIS light initial commit #1843
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
Open
Tschuppi81
wants to merge
19
commits into
master
Choose a base branch
from
feature/ogc-2245-ris-light
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+2,626
−1,872
Open
RIS light initial commit #1843
Changes from 9 commits
Commits
Show all changes
19 commits
Select commit
Hold shift + click to select a range
d77edcd
RIS light initial commit
Tschuppi81 77b4c4d
Add to most models
Tschuppi81 fcf66f9
Undo searchable for PoliticalBusinessParticipation
Tschuppi81 d032c35
Move lowest common donominator from ris and pas to parliament namespace
Tschuppi81 f7bea9f
Multiple small fixes
Tschuppi81 47bb63d
Puts subclasses for PAS namespace
Tschuppi81 8b8174b
Fix table name
Tschuppi81 eecb5f7
Initialize tests/parliament
Tschuppi81 48e1a75
Register module parliament and add upgrade step
Tschuppi81 d09ec75
re-add onegov-people cli
Tschuppi81 2c5d0dd
Adds column for polymorphic type
Tschuppi81 2416b19
Adjust collections
Tschuppi81 2e02fd7
Adjust tests
Tschuppi81 36ec823
Rework upgrade step and polymorphic type
Tschuppi81 a391296
Fix tests
Tschuppi81 e5818c8
Cleanup
Tschuppi81 9644460
Use models in corresponding views
Tschuppi81 11e78d2
Makes parliamentarian private
Tschuppi81 50bdfa7
Move Change::add to subclass
Tschuppi81 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
from __future__ import annotations | ||
|
||
import logging | ||
|
||
log = logging.getLogger('onegov.parliament') | ||
log.addHandler(logging.NullHandler()) | ||
|
||
from onegov.parliament.i18n import _ | ||
|
||
__all__ = ( | ||
'_', | ||
'log' | ||
) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
from __future__ import annotations | ||
|
||
from onegov.core.i18n.translation_string import TranslationStringFactory | ||
|
||
_ = TranslationStringFactory('onegov.parliament') |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
from __future__ import annotations | ||
|
||
from onegov.parliament.models.attendence import Attendence | ||
from onegov.parliament.models.change import Change | ||
from onegov.parliament.models.commission import Commission | ||
from onegov.parliament.models.commission_membership import CommissionMembership | ||
from onegov.parliament.models.legislative_period import LegislativePeriod | ||
from onegov.parliament.models.meeting import Meeting | ||
from onegov.parliament.models.parliamentarian import ( | ||
Parliamentarian, | ||
RISParliamentarian | ||
) | ||
from onegov.parliament.models.parliamentarian_role import ParliamentarianRole | ||
from onegov.parliament.models.parliamentary_group import ParliamentaryGroup | ||
from onegov.parliament.models.party import Party | ||
from onegov.parliament.models.political_business import ( | ||
PoliticalBusiness, | ||
PoliticalBusinessParticipation, | ||
) | ||
|
||
|
||
__all__ = ( | ||
'Attendence', | ||
'Change', | ||
'Commission', | ||
'CommissionMembership', | ||
'LegislativePeriod', | ||
'Meeting', | ||
'Parliamentarian', | ||
'RISParliamentarian', | ||
'ParliamentarianRole', | ||
'ParliamentaryGroup', | ||
'Party', | ||
'PoliticalBusiness', | ||
'PoliticalBusinessParticipation', | ||
) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,181 @@ | ||
from __future__ import annotations | ||
|
||
from decimal import ROUND_HALF_UP, Decimal | ||
|
||
from sqlalchemy import Column | ||
from sqlalchemy import Date | ||
from sqlalchemy import Enum | ||
from sqlalchemy import ForeignKey | ||
from sqlalchemy import Integer | ||
from sqlalchemy import Text | ||
from sqlalchemy.orm import relationship | ||
from uuid import uuid4 | ||
|
||
from onegov.core.orm import Base | ||
from onegov.core.orm.mixins import TimestampMixin | ||
from onegov.core.orm.types import UUID | ||
from onegov.parliament import _ | ||
|
||
from typing import TYPE_CHECKING | ||
|
||
if TYPE_CHECKING: | ||
import uuid | ||
import datetime | ||
from typing import Literal | ||
from typing import TypeAlias | ||
|
||
from onegov.parliament.models import Parliamentarian, Commission | ||
|
||
AttendenceType: TypeAlias = Literal[ | ||
'plenary', | ||
'commission', | ||
'study', | ||
'shortest', | ||
] | ||
|
||
TYPES: dict[AttendenceType, str] = { | ||
'plenary': _('Plenary session'), | ||
'commission': _('Commission meeting'), | ||
'study': _('File study'), | ||
'shortest': _('Shortest meeting'), | ||
} | ||
|
||
|
||
class Attendence(Base, TimestampMixin): | ||
|
||
__tablename__ = 'par_attendence' | ||
|
||
attendence_type: Column[str] = Column( | ||
Text, | ||
nullable=False, | ||
default=lambda: 'generic' | ||
) | ||
|
||
__mapper_args__ = { | ||
'polymorphic_on': attendence_type, | ||
'polymorphic_identity': 'generic', | ||
} | ||
|
||
#: Internal ID | ||
id: Column[uuid.UUID] = Column( | ||
UUID, # type:ignore[arg-type] | ||
primary_key=True, | ||
default=uuid4 | ||
) | ||
|
||
#: The date | ||
date: Column[datetime.date] = Column( | ||
Date, | ||
nullable=False | ||
) | ||
|
||
#: The duration in minutes | ||
duration: Column[int] = Column( | ||
Integer, | ||
nullable=False | ||
) | ||
|
||
#: The type | ||
type: Column[AttendenceType] = Column( | ||
Enum( | ||
*TYPES.keys(), # type:ignore[arg-type] | ||
name='par_attendence_type' | ||
), | ||
nullable=False, | ||
default='plenary' | ||
) | ||
|
||
#: The type as translated text | ||
@property | ||
def type_label(self) -> str: | ||
return TYPES.get(self.type, '') | ||
|
||
#: The id of the parliamentarian | ||
parliamentarian_id: Column[uuid.UUID] = Column( | ||
UUID, # type:ignore[arg-type] | ||
ForeignKey('par_parliamentarians.id'), | ||
nullable=False | ||
) | ||
|
||
#: The parliamentarian | ||
parliamentarian: relationship[Parliamentarian] = relationship( | ||
'Parliamentarian', | ||
back_populates='attendences' | ||
) | ||
|
||
#: the id of the commission | ||
commission_id: Column[uuid.UUID | None] = Column( | ||
UUID, # type:ignore[arg-type] | ||
ForeignKey('par_commissions.id'), | ||
nullable=True | ||
) | ||
|
||
#: the related commission (which may have any number of memberships) | ||
commission: relationship[Commission | None] = relationship( | ||
'Commission', | ||
back_populates='attendences' | ||
) | ||
|
||
def calculate_value(self) -> Decimal: | ||
"""Calculate the value (in hours) for an attendance record. | ||
|
||
The calculation follows these business rules: | ||
- Plenary sessions: | ||
* Always counted as 0.5 (half day), regardless of actual duration | ||
This is the special case! | ||
|
||
- Everything else is counted as actual hours: | ||
* First 2 hours are counted as given | ||
* After 2 hours, time is rounded to nearest 30-minute increment, | ||
* and there is another rate applied for the additional time | ||
* Example: 2h 40min would be calculated as 2.5 hours | ||
|
||
Examples: | ||
>>> # Plenary session | ||
>>> attendence.type = 'plenary' | ||
>>> calculate_value(attendence) | ||
'0.5' | ||
|
||
>>> # Commission meeting, 2 hours | ||
>>> attendence.type = 'commission' | ||
>>> attendence.duration = 120 # minutes | ||
>>> calculate_value(attendence) | ||
'2.0' | ||
|
||
>>> # Study session, 2h 40min | ||
>>> attendence.type = 'study' | ||
>>> attendence.duration = 160 # minutes | ||
>>> calculate_value(attendence) | ||
'2.5' | ||
""" | ||
if self.duration < 0: | ||
raise ValueError('Duration cannot be negative') | ||
|
||
if self.type == 'plenary': | ||
return Decimal('0.5') | ||
|
||
if self.type in ('commission', 'study', 'shortest'): | ||
# Convert minutes to hours with Decimal for precise calculation | ||
duration_hours = Decimal(str(self.duration)) / Decimal('60') | ||
|
||
if duration_hours <= Decimal('2'): | ||
# Round to 1 decimal place | ||
return duration_hours.quantize( | ||
Decimal('0.1'), rounding=ROUND_HALF_UP | ||
) | ||
else: | ||
base_hours = Decimal('2') | ||
additional_hours = (duration_hours - base_hours) | ||
# Round additional time to nearest 0.5 | ||
additional_hours = (additional_hours * 2).quantize( | ||
Decimal('1.0'), rounding=ROUND_HALF_UP | ||
) / 2 | ||
total_hours = base_hours + additional_hours | ||
return total_hours.quantize( | ||
Decimal('0.1'), rounding=ROUND_HALF_UP | ||
) | ||
|
||
raise ValueError(f'Unknown attendance type: {self.type}') | ||
|
||
def __repr__(self) -> str: | ||
return f'<Attendence {self.date} {self.type}>' | ||
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.