Skip to content

Commit 2097a63

Browse files
committed
feat#79: comprehensive analytics
1 parent bccaba0 commit 2097a63

2 files changed

Lines changed: 664 additions & 1 deletion

File tree

contract_/src/audition/season_and_audition.cairo

Lines changed: 345 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,57 @@ pub trait ISeasonAndAudition<TContractState> {
122122
fn is_audition_paused(self: @TContractState, audition_id: felt252) -> bool;
123123
fn is_audition_ended(self: @TContractState, audition_id: felt252) -> bool;
124124
fn audition_exists(self: @TContractState, audition_id: felt252) -> bool;
125+
126+
// Genre-based filtering
127+
fn get_seasons_by_genre(
128+
self: @TContractState, genre: felt252, max_results: u32,
129+
) -> Array<Season>;
130+
fn get_auditions_by_genre(
131+
self: @TContractState, genre: felt252, max_results: u32,
132+
) -> Array<Audition>;
133+
134+
// Time-based queries
135+
fn get_active_auditions(self: @TContractState, current_timestamp: u64) -> Array<Audition>;
136+
fn get_auditions_in_time_range(
137+
self: @TContractState, start_timestamp: u64, end_timestamp: u64,
138+
) -> Array<Audition>;
139+
140+
// Season-based queries
141+
fn get_auditions_by_season(self: @TContractState, season_id: felt252) -> Array<Audition>;
142+
143+
// Analytics functions
144+
fn get_audition_vote_count(self: @TContractState, audition_id: felt252) -> u32;
145+
fn get_total_vote_weight_for_performer(
146+
self: @TContractState, audition_id: felt252, performer: felt252,
147+
) -> felt252;
148+
149+
// Pagination and listing
150+
fn get_seasons_by_ids(self: @TContractState, season_ids: Array<felt252>) -> Array<Season>;
151+
fn get_auditions_by_ids(self: @TContractState, audition_ids: Array<felt252>) -> Array<Audition>;
152+
153+
// Complex filtering
154+
fn get_auditions_by_criteria(
155+
self: @TContractState,
156+
audition_ids: Array<felt252>,
157+
genre: Option<felt252>,
158+
season_id: Option<felt252>,
159+
start_time: Option<u64>,
160+
end_time: Option<u64>,
161+
paused_state: Option<bool>,
162+
) -> Array<Audition>;
163+
164+
// Utility functions
165+
fn is_audition_active(
166+
self: @TContractState, audition_id: felt252, current_timestamp: u64,
167+
) -> bool;
168+
fn get_audition_status(
169+
self: @TContractState, audition_id: felt252,
170+
) -> felt252; // 0=not_started, 1=active, 2=ended, 3=paused, 404= Not found
171+
fn count_votes_for_audition(
172+
self: @TContractState,
173+
audition_id: felt252,
174+
voter_performer_pairs: Array<(felt252, felt252)>,
175+
) -> u32;
125176
}
126177

127178
#[starknet::contract]
@@ -176,6 +227,16 @@ pub mod SeasonAndAudition {
176227
audition_winner_amounts: Map<felt252, (u256, u256, u256)>,
177228
/// price distributed status
178229
price_distributed: Map<felt252, bool>,
230+
// For efficient querying - maintain lists of IDs
231+
all_season_ids: Map<u32, felt252>, // index -> season_id
232+
all_audition_ids: Map<u32, felt252>, // index -> audition_id
233+
season_count: u32,
234+
audition_count: u32,
235+
// Vote counting cache
236+
audition_vote_counts: Map<felt252, u32>, // audition_id -> vote_count
237+
performer_total_weights: Map<
238+
(felt252, felt252), felt252,
239+
> // (audition_id, performer) -> total_weight
179240
}
180241

181242
#[event]
@@ -302,6 +363,10 @@ pub mod SeasonAndAudition {
302363
.entry(season_id)
303364
.write(Season { season_id, genre, name, start_timestamp, end_timestamp, paused });
304365

366+
let current_count = self.season_count.read();
367+
self.all_season_ids.write(current_count, season_id);
368+
self.season_count.write(current_count + 1);
369+
305370
self.emit(SeasonCreated { season_id, genre, name });
306371
}
307372

@@ -346,7 +411,9 @@ pub mod SeasonAndAudition {
346411
audition_id, season_id, genre, name, start_timestamp, end_timestamp, paused,
347412
},
348413
);
349-
414+
let current_count = self.audition_count.read();
415+
self.all_audition_ids.write(current_count, audition_id);
416+
self.audition_count.write(current_count + 1);
350417
self.emit(AuditionCreated { audition_id, season_id, genre, name });
351418
}
352419

@@ -555,6 +622,13 @@ pub mod SeasonAndAudition {
555622

556623
self.votes.entry(vote_key).write(Vote { audition_id, performer, voter, weight });
557624

625+
let current_count = self.audition_vote_counts.read(audition_id);
626+
self.audition_vote_counts.write(audition_id, current_count + 1);
627+
628+
let performer_key = (audition_id, performer);
629+
let current_weight = self.performer_total_weights.read(performer_key);
630+
self.performer_total_weights.write(performer_key, current_weight + weight);
631+
558632
self.emit(Event::VoteRecorded(VoteRecorded { audition_id, performer, voter, weight }));
559633
}
560634

@@ -656,6 +730,276 @@ pub mod SeasonAndAudition {
656730
let audition = self.auditions.entry(audition_id).read();
657731
audition.audition_id != 0
658732
}
733+
734+
// Genre-based filtering
735+
fn get_seasons_by_genre(
736+
self: @ContractState, genre: felt252, max_results: u32,
737+
) -> Array<Season> {
738+
let mut result = ArrayTrait::new();
739+
let mut count = 0;
740+
let total_seasons = self.season_count.read();
741+
742+
let mut i = 0;
743+
while i < total_seasons && count < max_results {
744+
let season_id = self.all_season_ids.read(i);
745+
if season_id != 0 {
746+
let season = self.seasons.read(season_id);
747+
if season.genre == genre {
748+
result.append(season);
749+
count += 1;
750+
}
751+
}
752+
i += 1;
753+
};
754+
755+
result
756+
}
757+
758+
fn get_auditions_by_genre(
759+
self: @ContractState, genre: felt252, max_results: u32,
760+
) -> Array<Audition> {
761+
let mut result = ArrayTrait::new();
762+
let mut count = 0;
763+
let total_auditions = self.audition_count.read();
764+
765+
let mut i = 0;
766+
while i < total_auditions && count < max_results {
767+
let audition_id = self.all_audition_ids.read(i);
768+
if audition_id != 0 {
769+
let audition = self.auditions.read(audition_id);
770+
if audition.genre == genre {
771+
result.append(audition);
772+
count += 1;
773+
}
774+
}
775+
i += 1;
776+
};
777+
778+
result
779+
}
780+
781+
// Time-based queries
782+
fn get_active_auditions(self: @ContractState, current_timestamp: u64) -> Array<Audition> {
783+
let mut result = ArrayTrait::new();
784+
let total_auditions = self.audition_count.read();
785+
786+
let mut i = 0;
787+
while i < total_auditions {
788+
let audition_id = self.all_audition_ids.read(i);
789+
if audition_id != 0 {
790+
let audition = self.auditions.read(audition_id);
791+
if self.is_audition_active(audition_id, current_timestamp.into()) {
792+
result.append(audition);
793+
}
794+
}
795+
i += 1;
796+
};
797+
798+
result
799+
}
800+
801+
fn get_auditions_in_time_range(
802+
self: @ContractState, start_timestamp: u64, end_timestamp: u64,
803+
) -> Array<Audition> {
804+
let mut result = ArrayTrait::new();
805+
let total_auditions: u32 = self.audition_count.read().try_into().unwrap();
806+
807+
let mut i = 0_u32;
808+
while i < total_auditions {
809+
let audition_id = self.all_audition_ids.read(i);
810+
if audition_id != 0 {
811+
let audition = self.auditions.read(audition_id);
812+
if audition.start_timestamp.try_into().unwrap() >= start_timestamp
813+
&& audition.end_timestamp.try_into().unwrap() <= end_timestamp {
814+
result.append(audition);
815+
}
816+
}
817+
i += 1;
818+
};
819+
820+
result
821+
}
822+
823+
// Season-based queries
824+
fn get_auditions_by_season(self: @ContractState, season_id: felt252) -> Array<Audition> {
825+
let mut result = ArrayTrait::new();
826+
let total_auditions = self.audition_count.read();
827+
828+
let mut i = 0;
829+
while i < total_auditions {
830+
let audition_id = self.all_audition_ids.read(i);
831+
if audition_id != 0 {
832+
let audition = self.auditions.read(audition_id);
833+
if audition.season_id == season_id {
834+
result.append(audition);
835+
}
836+
}
837+
i += 1;
838+
};
839+
840+
result
841+
}
842+
843+
// Analytics functions
844+
fn get_audition_vote_count(self: @ContractState, audition_id: felt252) -> u32 {
845+
self.audition_vote_counts.read(audition_id)
846+
}
847+
848+
fn get_total_vote_weight_for_performer(
849+
self: @ContractState, audition_id: felt252, performer: felt252,
850+
) -> felt252 {
851+
self.performer_total_weights.read((audition_id, performer))
852+
}
853+
854+
// Pagination and listing
855+
fn get_seasons_by_ids(self: @ContractState, season_ids: Array<felt252>) -> Array<Season> {
856+
let mut result = ArrayTrait::new();
857+
let season_ids_span = season_ids.span();
858+
859+
for season_id in season_ids_span {
860+
let season = self.seasons.read(*season_id);
861+
if season.season_id != 0 {
862+
result.append(season);
863+
}
864+
};
865+
866+
result
867+
}
868+
869+
fn get_auditions_by_ids(
870+
self: @ContractState, audition_ids: Array<felt252>,
871+
) -> Array<Audition> {
872+
let mut result = ArrayTrait::new();
873+
let audition_ids_span = audition_ids.span();
874+
875+
for audition_id in audition_ids_span {
876+
let audition = self.auditions.read(*audition_id);
877+
if audition.audition_id != 0 {
878+
result.append(audition);
879+
}
880+
};
881+
882+
result
883+
}
884+
885+
// Complex filtering
886+
fn get_auditions_by_criteria(
887+
self: @ContractState,
888+
audition_ids: Array<felt252>,
889+
genre: Option<felt252>,
890+
season_id: Option<felt252>,
891+
start_time: Option<u64>,
892+
end_time: Option<u64>,
893+
paused_state: Option<bool>,
894+
) -> Array<Audition> {
895+
let mut result = ArrayTrait::new();
896+
let audition_ids_span = audition_ids.span();
897+
898+
for audition_id in audition_ids_span {
899+
let audition = self.auditions.read(*audition_id);
900+
if audition.audition_id == 0 {
901+
continue;
902+
}
903+
904+
// Apply filters
905+
if let Option::Some(filter_genre) = genre {
906+
if audition.genre != filter_genre {
907+
continue;
908+
}
909+
}
910+
911+
if let Option::Some(filter_season_id) = season_id {
912+
if audition.season_id != filter_season_id {
913+
continue;
914+
}
915+
}
916+
917+
if let Option::Some(filter_start_time) = start_time {
918+
if audition.start_timestamp.try_into().unwrap() < filter_start_time {
919+
continue;
920+
}
921+
}
922+
923+
if let Option::Some(filter_end_time) = end_time {
924+
if audition.end_timestamp.try_into().unwrap() > filter_end_time {
925+
continue;
926+
}
927+
}
928+
929+
if let Option::Some(filter_paused) = paused_state {
930+
if audition.paused != filter_paused {
931+
continue;
932+
}
933+
}
934+
935+
result.append(audition);
936+
};
937+
938+
result
939+
}
940+
941+
// Utility functions
942+
fn is_audition_active(
943+
self: @ContractState, audition_id: felt252, current_timestamp: u64,
944+
) -> bool {
945+
let audition = self.auditions.read(audition_id);
946+
if audition.audition_id == 0 || audition.paused {
947+
return false;
948+
}
949+
950+
audition.start_timestamp.try_into().unwrap() <= current_timestamp
951+
&& (audition.end_timestamp == 0
952+
|| audition.end_timestamp.try_into().unwrap() > current_timestamp)
953+
}
954+
955+
/// returns 404 if not found
956+
/// 0 = Not started
957+
/// 1 = Active
958+
/// 2 = Ended
959+
/// 3 = Paused
960+
fn get_audition_status(self: @ContractState, audition_id: felt252) -> felt252 {
961+
let audition = self.auditions.read(audition_id);
962+
if audition.audition_id == 0 {
963+
return 404; // Not found
964+
}
965+
966+
if audition.paused {
967+
return 3; // Paused
968+
}
969+
970+
let current_time = get_block_timestamp();
971+
972+
if audition.start_timestamp.try_into().unwrap() > current_time {
973+
return 0; // Not started
974+
}
975+
976+
if audition.end_timestamp != 0
977+
&& audition.end_timestamp.try_into().unwrap() <= current_time {
978+
return 2; // Ended
979+
}
980+
981+
1 // Active
982+
}
983+
984+
fn count_votes_for_audition(
985+
self: @ContractState,
986+
audition_id: felt252,
987+
voter_performer_pairs: Array<(felt252, felt252)>,
988+
) -> u32 {
989+
let mut count = 0;
990+
let pairs_span = voter_performer_pairs.span();
991+
992+
for pair in pairs_span {
993+
let (performer, voter) = *pair;
994+
let vote_key = (audition_id, performer, voter);
995+
let vote = self.votes.read(vote_key);
996+
if vote.audition_id != 0 {
997+
count += 1;
998+
}
999+
};
1000+
1001+
count
1002+
}
6591003
}
6601004

6611005
#[generate_trait]

0 commit comments

Comments
 (0)