edoxen 0.7.2 → 2.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.rubocop.yml +17 -1
- data/.rubocop_todo.yml +1 -1
- data/CHANGELOG.md +246 -0
- data/README.adoc +308 -153
- data/edoxen.gemspec +10 -10
- data/lib/edoxen/action.rb +1 -1
- data/lib/edoxen/agenda.rb +4 -7
- data/lib/edoxen/agenda_item.rb +10 -3
- data/lib/edoxen/approval.rb +1 -1
- data/lib/edoxen/attendance.rb +16 -10
- data/lib/edoxen/body_vocabulary_entry.rb +24 -0
- data/lib/edoxen/body_vocabulary_host.rb +29 -0
- data/lib/edoxen/cli.rb +117 -120
- data/lib/edoxen/component_localization.rb +11 -0
- data/lib/edoxen/consideration.rb +2 -2
- data/lib/edoxen/contact.rb +24 -0
- data/lib/edoxen/contact_identifier.rb +13 -0
- data/lib/edoxen/contact_method.rb +16 -0
- data/lib/edoxen/decision.rb +69 -0
- data/lib/edoxen/decision_collection.rb +10 -0
- data/lib/edoxen/decision_date.rb +9 -0
- data/lib/edoxen/{resolution_metadata.rb → decision_metadata.rb} +5 -6
- data/lib/edoxen/decision_relation.rb +11 -0
- data/lib/edoxen/entity_ref.rb +74 -0
- data/lib/edoxen/enums.rb +117 -9
- data/lib/edoxen/error.rb +1 -1
- data/lib/edoxen/extension_attribute.rb +78 -0
- data/lib/edoxen/host_ref.rb +1 -1
- data/lib/edoxen/link_checker.rb +8 -8
- data/lib/edoxen/localization.rb +2 -2
- data/lib/edoxen/localization_host.rb +23 -0
- data/lib/edoxen/meeting.rb +63 -39
- data/lib/edoxen/meeting_collection.rb +1 -2
- data/lib/edoxen/meeting_collection_metadata.rb +3 -1
- data/lib/edoxen/meeting_component.rb +41 -0
- data/lib/edoxen/meeting_extension.rb +25 -0
- data/lib/edoxen/meeting_identifier.rb +1 -1
- data/lib/edoxen/meeting_localization.rb +6 -1
- data/lib/edoxen/meeting_series.rb +21 -0
- data/lib/edoxen/minutes.rb +2 -2
- data/lib/edoxen/minutes_section.rb +8 -11
- data/lib/edoxen/motion.rb +51 -0
- data/lib/edoxen/name.rb +29 -0
- data/lib/edoxen/officer.rb +19 -0
- data/lib/edoxen/officers_host.rb +19 -0
- data/lib/edoxen/person.rb +5 -9
- data/lib/edoxen/physical_venue.rb +15 -0
- data/lib/edoxen/recurrence.rb +27 -0
- data/lib/edoxen/recurrence_by_day.rb +10 -0
- data/lib/edoxen/reference_data.rb +35 -41
- data/lib/edoxen/schema_validator.rb +2 -3
- data/lib/edoxen/source_url.rb +3 -3
- data/lib/edoxen/structured_identifier.rb +2 -2
- data/lib/edoxen/topic.rb +37 -0
- data/lib/edoxen/topic_asset.rb +14 -0
- data/lib/edoxen/topic_document.rb +16 -0
- data/lib/edoxen/venue.rb +80 -0
- data/lib/edoxen/venue_validator.rb +56 -0
- data/lib/edoxen/version.rb +1 -1
- data/lib/edoxen/virtual_venue.rb +15 -0
- data/lib/edoxen/vote_record.rb +17 -13
- data/lib/edoxen/voting.rb +45 -0
- data/lib/edoxen/voting_counts.rb +23 -0
- data/lib/edoxen.rb +40 -13
- data/schema/edoxen.yaml +792 -145
- data/schema/meeting.yaml +600 -142
- data/sig/edoxen.rbs +581 -1
- metadata +57 -20
- data/lib/edoxen/_metadata.rb.deprecated +0 -57
- data/lib/edoxen/location.rb +0 -15
- data/lib/edoxen/resolution.rb +0 -43
- data/lib/edoxen/resolution_collection.rb +0 -10
- data/lib/edoxen/resolution_date.rb +0 -11
- data/lib/edoxen/resolution_relation.rb +0 -11
- data/lib/edoxen/resolution_set.rb +0 -17
- data/lib/edoxen/schedule_item.rb +0 -27
- data/lib/edoxen/schedule_item_localization.rb +0 -21
data/lib/edoxen/venue.rb
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Edoxen
|
|
4
|
+
# Venue — polymorphic by discriminator, flat on the wire.
|
|
5
|
+
#
|
|
6
|
+
# The `kind` field discriminates between physical and virtual venues.
|
|
7
|
+
# All possible fields (physical-specific and virtual-specific) live on
|
|
8
|
+
# this one class as optional attributes — only the ones matching the
|
|
9
|
+
# `kind` are populated. This avoids lutaml-model's polymorphic
|
|
10
|
+
# recursion pitfalls while keeping the wire format readable.
|
|
11
|
+
#
|
|
12
|
+
# The PhysicalVenue and VirtualVenue subclasses exist for type-checking
|
|
13
|
+
# and for carrying type-specific helpers (e.g. `#unlocode_entry`).
|
|
14
|
+
# Construct them programmatically; the YAML wire format always uses
|
|
15
|
+
# the flat shape via Venue.
|
|
16
|
+
#
|
|
17
|
+
# Replaces v0.x `Location` (physical-only) and `Meeting.virtual: Boolean`
|
|
18
|
+
# (insufficient — Zoom needs URL+passcode+dial-in).
|
|
19
|
+
class Venue < Lutaml::Model::Serializable
|
|
20
|
+
attribute :kind, :string, values: Enums::VENUE_KIND
|
|
21
|
+
attribute :name, :string
|
|
22
|
+
attribute :label, :string
|
|
23
|
+
attribute :description, :string
|
|
24
|
+
attribute :capacity, :integer
|
|
25
|
+
attribute :url, :string
|
|
26
|
+
attribute :contact_methods, ContactMethod, collection: true
|
|
27
|
+
|
|
28
|
+
# Physical-venue fields (populated when kind == "physical").
|
|
29
|
+
attribute :unlocode, :string
|
|
30
|
+
attribute :iata_code, :string
|
|
31
|
+
attribute :address, :string
|
|
32
|
+
attribute :city, :string
|
|
33
|
+
attribute :country_code, :string
|
|
34
|
+
attribute :lat, :float
|
|
35
|
+
attribute :lon, :float
|
|
36
|
+
attribute :building, :string
|
|
37
|
+
attribute :floor, :string
|
|
38
|
+
attribute :room, :string
|
|
39
|
+
attribute :access_notes, :string
|
|
40
|
+
|
|
41
|
+
# Virtual-venue fields (populated when kind == "virtual").
|
|
42
|
+
attribute :uri, :string
|
|
43
|
+
attribute :features, :string, collection: true, values: Enums::VIRTUAL_FEATURE
|
|
44
|
+
attribute :passcode, :string
|
|
45
|
+
attribute :meeting_id, :string
|
|
46
|
+
attribute :dial_in_numbers, :string, collection: true
|
|
47
|
+
attribute :waiting_room, :boolean
|
|
48
|
+
attribute :registration_required, :boolean
|
|
49
|
+
|
|
50
|
+
attribute :extensions, MeetingExtension, collection: true
|
|
51
|
+
|
|
52
|
+
def physical?
|
|
53
|
+
kind == "physical"
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def virtual?
|
|
57
|
+
kind == "virtual"
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# Resolve the UN/LOCODE entry via the canonical `unlocodes` gem.
|
|
61
|
+
# Returns an Unlocodes::Entry or nil when the code is empty / unknown.
|
|
62
|
+
def unlocode_entry
|
|
63
|
+
return nil if unlocode.nil? || unlocode.to_s.empty?
|
|
64
|
+
|
|
65
|
+
Edoxen::ReferenceData.find_unlocode(unlocode)
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def iata_entry
|
|
69
|
+
return nil if iata_code.nil? || iata_code.to_s.empty?
|
|
70
|
+
|
|
71
|
+
Edoxen::ReferenceData.find_iata(iata_code)
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def features_list
|
|
75
|
+
return "" if features.nil? || features.empty?
|
|
76
|
+
|
|
77
|
+
features.join(", ")
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
end
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Edoxen
|
|
4
|
+
# VenueValidator — validates Venue instances using the `unlocodes`
|
|
5
|
+
# and `iata` gems. Returns a list of errors; empty list means valid.
|
|
6
|
+
#
|
|
7
|
+
# For PhysicalVenue with a `unlocode` or `iata_code` field, validates
|
|
8
|
+
# that the code exists in the canonical registries. Optionally
|
|
9
|
+
# auto-populates city/country_code/coordinates from the registry.
|
|
10
|
+
class VenueValidator
|
|
11
|
+
attr_reader :venue, :errors
|
|
12
|
+
|
|
13
|
+
def initialize(venue)
|
|
14
|
+
@venue = venue
|
|
15
|
+
@errors = []
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def validate(auto_populate: false)
|
|
19
|
+
validate_physical(auto_populate: auto_populate) if venue.physical?
|
|
20
|
+
|
|
21
|
+
errors
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def valid?
|
|
25
|
+
validate
|
|
26
|
+
errors.empty?
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
private
|
|
30
|
+
|
|
31
|
+
def validate_physical(auto_populate:)
|
|
32
|
+
validate_unlocode(auto_populate: auto_populate) if venue.unlocode && !venue.unlocode.to_s.empty?
|
|
33
|
+
validate_iata_code if venue.iata_code && !venue.iata_code.to_s.empty?
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def validate_unlocode(auto_populate:)
|
|
37
|
+
entry = Edoxen::ReferenceData.find_unlocode(venue.unlocode)
|
|
38
|
+
if entry.nil?
|
|
39
|
+
errors << "Unknown UN/LOCODE: #{venue.unlocode}"
|
|
40
|
+
return
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
return unless auto_populate
|
|
44
|
+
|
|
45
|
+
venue.city ||= entry.name
|
|
46
|
+
venue.country_code ||= entry.country
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def validate_iata_code
|
|
50
|
+
entry = Edoxen::ReferenceData.find_iata(venue.iata_code)
|
|
51
|
+
return if entry
|
|
52
|
+
|
|
53
|
+
errors << "Unknown IATA code: #{venue.iata_code}"
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
data/lib/edoxen/version.rb
CHANGED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Edoxen
|
|
4
|
+
# VirtualVenue — type marker for a Venue with kind == "virtual".
|
|
5
|
+
#
|
|
6
|
+
# The wire format is flat on Venue (all fields live there). This
|
|
7
|
+
# subclass exists for type-checking and to host virtual-only helpers
|
|
8
|
+
# so callers can ask `venue.is_a?(VirtualVenue)` and validators
|
|
9
|
+
# can dispatch by type.
|
|
10
|
+
class VirtualVenue < Venue
|
|
11
|
+
def initialize(attributes = {})
|
|
12
|
+
super(attributes.merge(kind: "virtual"))
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
data/lib/edoxen/vote_record.rb
CHANGED
|
@@ -1,25 +1,29 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module Edoxen
|
|
4
|
-
# A single vote on a
|
|
5
|
-
#
|
|
6
|
-
#
|
|
7
|
-
#
|
|
8
|
-
#
|
|
9
|
-
#
|
|
4
|
+
# A single vote on a Decision, recorded against the Voting instance
|
|
5
|
+
# that captured it. `voting_ref` links to the Voting URN; `decision_ref`
|
|
6
|
+
# links to the Decision URN (often derivable from the Voting's motion,
|
|
7
|
+
# but explicit for clarity).
|
|
8
|
+
#
|
|
9
|
+
# `role` is open for adopter-defined values: teller (parliamentary
|
|
10
|
+
# division counter), proxy_holder, observer, etc.
|
|
10
11
|
class VoteRecord < Lutaml::Model::Serializable
|
|
11
|
-
attribute :
|
|
12
|
+
attribute :decision_ref, :string
|
|
13
|
+
attribute :voting_ref, :string
|
|
12
14
|
attribute :person, Person
|
|
13
15
|
attribute :affiliation, :string
|
|
14
16
|
attribute :vote, :string, values: Enums::VOTE_TYPE
|
|
17
|
+
attribute :role, :string
|
|
15
18
|
attribute :notes, :string
|
|
19
|
+
attribute :extensions, MeetingExtension, collection: true
|
|
16
20
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
21
|
+
def affirmative?
|
|
22
|
+
vote == "affirmative"
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def teller?
|
|
26
|
+
role == "teller"
|
|
23
27
|
end
|
|
24
28
|
end
|
|
25
29
|
end
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Edoxen
|
|
4
|
+
# Voting — state machine for a single vote on a Motion.
|
|
5
|
+
# Multiple votings can occur on the same Motion (e.g., unclear voice
|
|
6
|
+
# vote → chair calls a formal division → second Voting instance).
|
|
7
|
+
#
|
|
8
|
+
# State machine (VotingStatus):
|
|
9
|
+
# called → in_progress → decided | withdrawn | deferred
|
|
10
|
+
class Voting < Lutaml::Model::Serializable
|
|
11
|
+
attribute :identifier, :string
|
|
12
|
+
attribute :urn, :string
|
|
13
|
+
attribute :on_motion, :string
|
|
14
|
+
attribute :status, :string, values: Enums::VOTING_STATUS
|
|
15
|
+
attribute :voting_method, :string, values: Enums::VOTING_METHOD
|
|
16
|
+
attribute :called_by, Person
|
|
17
|
+
attribute :called_at, :date_time
|
|
18
|
+
attribute :result_declared_at, :date_time
|
|
19
|
+
attribute :result, :string, values: Enums::VOTING_OUTCOME
|
|
20
|
+
attribute :counts, VotingCounts
|
|
21
|
+
attribute :casting_vote, VoteRecord
|
|
22
|
+
attribute :vote_records, VoteRecord, collection: true
|
|
23
|
+
attribute :extensions, MeetingExtension, collection: true
|
|
24
|
+
|
|
25
|
+
def decided?
|
|
26
|
+
status == "decided"
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def in_progress?
|
|
30
|
+
status == "in_progress"
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def passed?
|
|
34
|
+
decided? && result == "passed"
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def negatived?
|
|
38
|
+
decided? && result == "negatived"
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def tied?
|
|
42
|
+
decided? && result == "tied"
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Edoxen
|
|
4
|
+
# VotingCounts — tally for a Voting instance.
|
|
5
|
+
class VotingCounts < Lutaml::Model::Serializable
|
|
6
|
+
attribute :ayes, :integer
|
|
7
|
+
attribute :noes, :integer
|
|
8
|
+
attribute :abstentions, :integer
|
|
9
|
+
attribute :absent, :integer
|
|
10
|
+
|
|
11
|
+
def total
|
|
12
|
+
(ayes || 0) + (noes || 0) + (abstentions || 0) + (absent || 0)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def margin
|
|
16
|
+
(ayes || 0) - (noes || 0)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def tied?
|
|
20
|
+
ayes == noes
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
data/lib/edoxen.rb
CHANGED
|
@@ -18,36 +18,41 @@ module Edoxen
|
|
|
18
18
|
# There are intentionally NO `require_relative` calls in this gem —
|
|
19
19
|
# autoload keeps load-order semantics clean and lets us tolerate the
|
|
20
20
|
# extensive cross-references between model classes
|
|
21
|
-
# (
|
|
21
|
+
# (Decision <-> Localization, DecisionMetadata <-> Localization, etc.).
|
|
22
22
|
autoload :VERSION, "edoxen/version"
|
|
23
23
|
autoload :Error, "edoxen/error"
|
|
24
24
|
autoload :ValidationError, "edoxen/error"
|
|
25
25
|
autoload :Enums, "edoxen/enums"
|
|
26
26
|
autoload :ReferenceData, "edoxen/reference_data"
|
|
27
27
|
|
|
28
|
-
#
|
|
29
|
-
|
|
28
|
+
# --- Shared behaviours (mixed into entity + metadata classes) --------
|
|
29
|
+
autoload :BodyVocabularyHost, "edoxen/body_vocabulary_host"
|
|
30
|
+
autoload :LocalizationHost, "edoxen/localization_host"
|
|
31
|
+
autoload :OfficersHost, "edoxen/officers_host"
|
|
32
|
+
|
|
33
|
+
# --- Decision side ----------------------------------------------------
|
|
30
34
|
autoload :StructuredIdentifier, "edoxen/structured_identifier"
|
|
31
35
|
autoload :MeetingIdentifier, "edoxen/meeting_identifier"
|
|
32
|
-
autoload :
|
|
36
|
+
autoload :DecisionDate, "edoxen/decision_date"
|
|
33
37
|
autoload :Action, "edoxen/action"
|
|
34
38
|
autoload :Approval, "edoxen/approval"
|
|
35
39
|
autoload :Consideration, "edoxen/consideration"
|
|
36
40
|
autoload :SourceUrl, "edoxen/source_url"
|
|
37
41
|
autoload :Localization, "edoxen/localization"
|
|
38
42
|
autoload :Url, "edoxen/url"
|
|
39
|
-
autoload :
|
|
40
|
-
autoload :
|
|
41
|
-
autoload :
|
|
42
|
-
autoload :
|
|
43
|
+
autoload :DecisionRelation, "edoxen/decision_relation"
|
|
44
|
+
autoload :Decision, "edoxen/decision"
|
|
45
|
+
autoload :DecisionMetadata, "edoxen/decision_metadata"
|
|
46
|
+
autoload :DecisionCollection, "edoxen/decision_collection"
|
|
43
47
|
|
|
44
|
-
# Meeting
|
|
48
|
+
# --- Meeting/Agenda side ----------------------------------------------
|
|
45
49
|
autoload :DateRange, "edoxen/date_range"
|
|
46
|
-
autoload :
|
|
50
|
+
autoload :ContactMethod, "edoxen/contact_method"
|
|
51
|
+
autoload :ContactIdentifier, "edoxen/contact_identifier"
|
|
52
|
+
autoload :Name, "edoxen/name"
|
|
53
|
+
autoload :Contact, "edoxen/contact"
|
|
47
54
|
autoload :Person, "edoxen/person"
|
|
48
55
|
autoload :HostRef, "edoxen/host_ref"
|
|
49
|
-
autoload :ScheduleItemLocalization, "edoxen/schedule_item_localization"
|
|
50
|
-
autoload :ScheduleItem, "edoxen/schedule_item"
|
|
51
56
|
autoload :Deadline, "edoxen/deadline"
|
|
52
57
|
autoload :Reference, "edoxen/reference"
|
|
53
58
|
autoload :AgendaItem, "edoxen/agenda_item"
|
|
@@ -62,7 +67,29 @@ module Edoxen
|
|
|
62
67
|
autoload :MeetingCollectionMetadata, "edoxen/meeting_collection_metadata"
|
|
63
68
|
autoload :MeetingCollection, "edoxen/meeting_collection"
|
|
64
69
|
|
|
65
|
-
#
|
|
70
|
+
# --- v2 broadened-scope entities --------------------------------------
|
|
71
|
+
autoload :ExtensionAttribute, "edoxen/extension_attribute"
|
|
72
|
+
autoload :MeetingExtension, "edoxen/meeting_extension"
|
|
73
|
+
autoload :EntityRef, "edoxen/entity_ref"
|
|
74
|
+
autoload :BodyVocabularyEntry, "edoxen/body_vocabulary_entry"
|
|
75
|
+
autoload :Venue, "edoxen/venue"
|
|
76
|
+
autoload :PhysicalVenue, "edoxen/physical_venue"
|
|
77
|
+
autoload :VirtualVenue, "edoxen/virtual_venue"
|
|
78
|
+
autoload :Motion, "edoxen/motion"
|
|
79
|
+
autoload :VotingCounts, "edoxen/voting_counts"
|
|
80
|
+
autoload :Voting, "edoxen/voting"
|
|
81
|
+
autoload :TopicDocument, "edoxen/topic_document"
|
|
82
|
+
autoload :TopicAsset, "edoxen/topic_asset"
|
|
83
|
+
autoload :Topic, "edoxen/topic"
|
|
84
|
+
autoload :RecurrenceByDay, "edoxen/recurrence_by_day"
|
|
85
|
+
autoload :Recurrence, "edoxen/recurrence"
|
|
86
|
+
autoload :MeetingSeries, "edoxen/meeting_series"
|
|
87
|
+
autoload :ComponentLocalization, "edoxen/component_localization"
|
|
88
|
+
autoload :MeetingComponent, "edoxen/meeting_component"
|
|
89
|
+
autoload :Officer, "edoxen/officer"
|
|
90
|
+
autoload :VenueValidator, "edoxen/venue_validator"
|
|
91
|
+
|
|
92
|
+
# --- Services ---------------------------------------------------------
|
|
66
93
|
autoload :SchemaValidator, "edoxen/schema_validator"
|
|
67
94
|
autoload :LinkChecker, "edoxen/link_checker"
|
|
68
95
|
autoload :Cli, "edoxen/cli"
|